%%%-------------------------------------------------------------------
%%% @doc    废土战场(饥饿游戏)
%%% Module  : lib_wasteland_war.erl
%%% Created : 2020-08-03
%%% @Author : tyl
%%%-------------------------------------------------------------------
-module(lib_wasteland_war).

-include("common.hrl").
-include("server.hrl").
-include("scene.hrl").
-include("battle.hrl").
-include("figure.hrl").
-include("role.hrl").
-include("errcode.hrl").
-include("def_module.hrl").
-include("predefine.hrl").
-include("wasteland_war.hrl").
-include("def_goods.hrl").
-include("goods.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").
-include("activitycalen.hrl").

%% API
-export([
	create_zone_worker/1, calc_war_field/2, get_act_cls_type/0, get_server_zone/1, for_each_zone/5, close_old_worker/1, %get_custom_act/0, is_act_open/0,
	get_role_list/1, trans_to_role/8, logout/1
	%% 场景相关接口
	,get_round_scene/1, get_all_pools/2, get_role_pool/2, get_ready_pool/1, get_war_pos_list/1, get_war_pos/1, switch_scene_callback/2
	,is_in_wasteland_scene/1, get_scene_born_xy/1, is_in_scene/2, is_wasteland_scene/1, get_default_scene/0
	,get_rand_born_xy/1, is_in_hosting/4, is_die/2, change_scene_callback/4, switch_default_scene/2
	%% 进程字典存场景玩家
	,proc_put_all_scene_roles/2, proc_get_scene_roles/1, proc_put_scene_roles/2, proc_get_exit_roles/1, proc_put_exit_roles/2
	%% 结算相关
	,settle_reward_and_finish_war/4, settle_reward_and_take_next_round/3, send_rank_reward/2, send_round_reward/3
	%% 玩法相关
	,create_crystal/3, when_crystal_die/5, calc_killer_obtain_score/3, check_stop_combo/1, check_kill_combo/1, kill_player/5
	,player_be_hurted/2, clean_hurt_list/1, clean_hurt_list_on_node/1, clean_hurt_on_player/1, add_assist_score/5, recalc_role_rank/5
	,kill_crystal_add_combo/2, get_hp_lim/2, revive_player_on_node/2, sort_role_rank/2, sort_war_role_rank/1
	,clean_scene_player/2, save_role_list/1
	%% 托管相关
	,start_hosting/1, wasteland_war_hosting_local/0
	%% 推送相关
	,send_to_client/3, send_to_client/4, notice_kill_combo/4, notice_stop_kill/3, notice_combo_tv/6, notice_end_combo_tv/8
	,notice_act_start/3, notice_act_start_local/2, notice_act_end/4, notice_act_change/3, notice_act_change_local/2, notice_act_end_local/3
	,notice_combo_tv_local/1, notice_end_combo_tv_local/1
	%% 数据库操作相关
	,db_update_rank_round/3, db_delete_rank/0,db_update_rank_name/3, db_update_rank_vip/3, db_update_rank_sup_vip/3, db_update_rank_power/3
	%% 活动控制相关
	,act_start/1, is_local_start/1%,  is_cls_act_open/0
	%% TA数据相关记录
	,pro_record_enter_time/1, pro_add_kill_role_score/2, pro_add_kill_crystal_score/2, pro_record_revive_times/1, pro_get_role_ta_data/1
]).

%% -------------------------------------------------------------------
%% @doc 分战场
%% -------------------------------------------------------------------
%% 分战场规则:
%% 1.在准备阶段结束时，将所有在场景内的玩家（包括机器人）战力进行排名
%% 2.先按照一个战场X个人的方式算出需要多少个战场（进一法取值）
%% 3.然后按照排名次序，从第一名开始往每个战场分配，直到所有战场都有1个人，然后再给第一个战场分配第二个人以此类推
%% 直到所有人都有对应战场
%% 场景分线规则：PoolId = ZoneId * ?SCENE_POOL_OFFSET + WarFieldNum  即 分区Id * 2000 + 战场号

%% 根据战力计算分战场
calc_war_field(ZoneId, ZoneWorkerMap) ->
	case maps:get(ZoneId, ZoneWorkerMap, []) of
		WorkerPid when is_pid(WorkerPid) ->
			mod_wasteland_worker:calc_war_field(WorkerPid);
		_ ->
			ignore
	end,
	ok.

%% 创建分区管理进程失败需要通知玩家
create_zone_worker(ZoneId)->
	case mod_wasteland_worker:start_link(ZoneId) of
		{ok, Pid} -> CreateTrue = true;
		{_, {already_started, Pid}}  -> CreateTrue = true;
		_                       -> CreateTrue = false, Pid = []
	end,
	{CreateTrue, Pid}.

%% 进入场景转换成战场角色数据
trans_to_role(Node, RoleId, Figure, ServerId, ServerNum, IsHosting, HpLim, PoolId) ->
	#figure{name = Name, power = Power, sex = Sex, vip = VipLv,
		sup_vip = SupVip} = Figure,
	#role_info{
		role_id = RoleId ,role_name  = Name,  power = Power
		,sex    = Sex    ,vip_lv     = VipLv, sup_vip = SupVip
		,server_id  = ServerId, server_num = ServerNum, node = Node
		,figure = Figure, is_hosting = IsHosting, hp_lim = HpLim
		,ready_pool = PoolId
	}.

%% -------------------------------------------------------------------
%% 退出/掉线(暂时未用)
logout(RoleId) ->
	ServerId = config:get_server_id(),
	Node = clusters_node:get_node(),
	mod_wasteland_war:quit_scene(Node, ServerId, RoleId).

%% 获取服务器的分区状态来判断是否进入跨服分区模式
get_act_cls_type() ->
	case czone_api:zone_open_check() of
		{true, ZoneId} when is_integer(ZoneId) andalso ZoneId > 0 ->
			?WAR_TYPE_CLS;
		_ ->
			?WAR_TYPE_LOCAL
	end.

%% 获取服务器Id对应的ZoneId(本服玩法赋值为1)
get_server_zone(ServerId) ->
	case config:get_cls_type() of
		1 -> czone:get_zone_id(ServerId);
		_ -> 1
	end.

%% 对每一分区执行函数
for_each_zone(ZoneMap, Mod, Fun, Args, RetArgs) ->
	Values = maps:values(ZoneMap),
	F = fun(Value, TReturnArgs) ->
		apply(Mod, Fun, [Value|Args] ++ TReturnArgs)
	end,
	lists:foldl(F, RetArgs, Values).

close_old_worker(OldWorkerMap) when is_map(OldWorkerMap) ->
	%% 销毁旧的分区管理进程
	F0 = fun(_TZoneId, WorkPid) ->
		case is_pid(WorkPid) of
			true ->
				mod_wasteland_worker:close_worker(WorkPid);
			_ ->
				ignore
		end end,
	maps:map(F0, OldWorkerMap);
close_old_worker(_) ->
	ok.

%%%% 获取定制的玩法天数,满足条件则在本服玩,其他跟据跨服节奏
%%get_custom_act() ->
%%	OpenDay  = util:get_open_day(),
%%	MergeDay = util:get_merge_day(),
%%	case MergeDay > 0 of
%%		false ->
%%			case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, 1) of
%%				#base_ac{open_day = [{StartDay, EndDay}]} when OpenDay >= StartDay andalso OpenDay =< EndDay ->
%%					true;
%%				_ ->
%%					false
%%			end;
%%		_ ->
%%			case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, 3) of
%%				#base_ac{merge_day = [{MergeStartDay, MergeEndDay}]} when MergeDay >= MergeStartDay andalso MergeDay =< MergeEndDay ->
%%					true;
%%				_ ->
%%					false
%%			end
%%	end.
%%
%%%% 判断是否在活动天数内
%%is_act_open() ->
%%	OpenDay  = util:get_open_day(),
%%	MergeDay = util:get_merge_day(),
%%	% 判断是否合过服
%%	case MergeDay > 0 of
%%		false ->
%%			case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, 1) of
%%				#base_ac{open_day = [{StartDay, EndDay}]} when OpenDay >= StartDay andalso OpenDay =< EndDay ->
%%					true;
%%				_ ->
%%					case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, 2) of
%%						#base_ac{open_day = [{ClsStartDay, ClsEndDay}]} when OpenDay >= ClsStartDay andalso OpenDay =< ClsEndDay ->
%%							true;
%%						_ ->
%%							case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, 3) of
%%								#base_ac{open_day = [{ClsStartDay, ClsEndDay}]} when OpenDay >= ClsStartDay andalso OpenDay =< ClsEndDay ->
%%									true;
%%								_ ->
%%									false
%%							end
%%					end
%%			end;
%%		_ ->
%%			case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, 3) of
%%				#base_ac{merge_day = [{StartDay, EndDay}]} when MergeDay >= StartDay andalso MergeDay =< EndDay ->
%%					true;
%%				_ ->
%%					case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, 4) of
%%						#base_ac{merge_day = [{ClsStartDay, ClsEndDay}]} when MergeDay >= ClsStartDay andalso MergeDay =< ClsEndDay ->
%%							true;
%%						_ ->
%%							false
%%					end
%%			end
%%	end.
%%
%%%% 判断是否跨服节奏
%%is_cls_act_open() ->
%%	OpenDay  = util:get_open_day(),
%%	MergeDay = util:get_merge_day(),
%%	% 判断是否合过服
%%	case MergeDay > 0 of
%%		false ->
%%			case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, 2) of
%%				#base_ac{open_day = [{ClsStartDay, ClsEndDay}]} when OpenDay >= ClsStartDay andalso OpenDay =< ClsEndDay ->
%%					%% 如果本服开了定制,不走跨服节奏
%%					case is_local_act_open() of
%%						true -> false;
%%						_    -> true
%%					end;
%%				_ ->
%%					false
%%			end;
%%		_ ->
%%			case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, 4) of
%%				#base_ac{merge_day = [{MergeStartDay, MergeEndDay}]} when MergeDay >= MergeStartDay andalso MergeDay =< MergeEndDay ->
%%					%% 如果本服开了定制,不走跨服节奏
%%					case is_local_act_open() of
%%						true -> false;
%%						_    -> true
%%					end;
%%				_ ->
%%					false
%%			end
%%	end.
%%
%%%% 判断是否本服节奏
%%is_local_act_open() ->
%%	OpenDay  = util:get_open_day(),
%%	MergeDay = util:get_merge_day(),
%%	% 判断是否合过服
%%	case MergeDay > 0 of
%%		false ->
%%			case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, 1) of
%%				#base_ac{open_day = [{StartDay, EndDay}]} when OpenDay >= StartDay andalso OpenDay =< EndDay ->
%%					true;
%%				_ ->
%%					false
%%			end;
%%		_ ->
%%			case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, 3) of
%%				#base_ac{merge_day = [{MergeStartDay, MergeEndDay}]} when MergeDay >= MergeStartDay andalso MergeDay =< MergeEndDay ->
%%					true;
%%				_ ->
%%					false
%%			end
%%	end.

%% -------------------------------------------------------------------
%% @doc 场景相关
%% -------------------------------------------------------------------
%% 是否在场景中
is_in_wasteland_scene(SceneId) ->
	ClsScene   = data_wasteland_war:get_cls_all_scene(),
	LocalScene = data_wasteland_war:get_all_scene(),
	AllScenes = ClsScene ++ LocalScene,
	case lists:member(SceneId, AllScenes) of
		true -> true;
		_ ->    false
	end.

%% 是否在玩法中
is_in_scene(RoleId, RoleMap) ->
	case maps:get(RoleId, RoleMap, []) of
		#role_info{is_in_scene = ?IN_SCENE_YES} -> true;
		_ -> false
	end.

%% 是否废土场景
is_wasteland_scene(SceneId) ->
	ClsScenes  = data_wasteland_war:get_cls_all_scene(),
	LocalScenes = data_wasteland_war:get_all_scene(),
	case lists:member(SceneId, ClsScenes++LocalScenes) of
		true -> true;
		_ ->    false
	end.

get_round_scene(Round) ->
	ClsType = config:get_cls_type(),
	case data_wasteland_war:get_round_cfg(Round) of
		#wasteland_round_reward_cfg{scene = LocalScene, cls_scene = ClsScene} ->
			case ClsType of
				1 -> Scene = ClsScene;
				_ -> Scene = LocalScene
			end;
		_ ->
			case ClsType of
				1 ->
					Scene = ?CLS_WAR_DEFAULT_SCENE;
				_ ->
					Scene = ?LOCAL_WAR_DEFAULT_SCENE
			end
	end,
	Scene.

get_default_scene() ->
	case config:get_cls_type() of
		1 ->
			Scene = ?CLS_WAR_DEFAULT_SCENE;
		_ ->
			Scene = ?LOCAL_WAR_DEFAULT_SCENE
	end,
	Scene.

get_all_pools(ZoneId, WarFieldMap) ->
	WarFieldNums = maps:keys(WarFieldMap),
	F = fun(WarFieldNum, TList) ->
		PoolId  = get_role_pool(ZoneId, WarFieldNum),
		[PoolId|lists:delete(PoolId, TList)]
	end,
	lists:foldl(F, [], WarFieldNums).

get_role_pool(ZoneId, WarFieldNum) ->
	PoolId = ZoneId * ?SCENE_POOL_OFFSET + WarFieldNum,
	PoolId.

get_ready_pool(ZoneId) ->
	PoolId = ZoneId * ?SCENE_POOL_OFFSET,
	PoolId.

get_war_pos_list(Scene) ->
	case data_wasteland_war:get_pos_list(Scene) of
		#wasteland_select_pos_cfg{war_pos_list = WarPosList} ->
			case WarPosList of
				[{_, _, _}|_] -> [{X, Y}||{X, Y, _R}<-WarPosList];
				[{_, _}|_] -> WarPosList;
				_ ->
					[{4977,3263}]
			end;
		_ -> []

	end.

get_war_pos(Scene) ->
	case data_wasteland_war:get_pos_list(Scene) of
		#wasteland_select_pos_cfg{war_pos_list = [_|_] = List} ->
			RandPos = urand:list_rand(List),
			case RandPos of
				{X, Y, _} -> {X, Y};
				{X, Y} -> {X, Y};
				_ -> {4977,3263}
			end;
		_ -> {4977,3263}
	end.

%% 切换场景回调
switch_scene_callback(Node, Args) ->
	unode:apply(Node, lib_scene, change_player_scene, Args).

%% 切换到默认场景(修复场景)
switch_default_scene(Node, RoleId) ->
	unode:apply(Node, lib_scene, player_change_default_scene, [RoleId, []]).

%% 获取场景出生点
get_scene_born_xy(SceneId) ->
	[X, Y] = lib_scene:get_born_xy(SceneId),
	{X, Y}.

%% 获取托管玩家和结晶的出生点
get_rand_born_xy(SceneId) ->
	{X, Y} =
	case data_wasteland_war:get_pos_list(SceneId) of
		#wasteland_select_pos_cfg{pos_list = [{CenterX, CenterY, Radius}]} ->
			umath:rand_point_in_circle(CenterX, CenterY, Radius, 1);
		#wasteland_select_pos_cfg{pos_list = [_|_] = PosList} ->
			{CenterX, CenterY, _Radius} = urand:list_rand(PosList),
			{CenterX, CenterY};
		_ -> {0, 0}
	end,
	{X, Y}.

%% 是否在托管中(废弃)
is_in_hosting(RoleId, ServerId, SrvZoneMap, RoleMap) ->
	ZoneId   = maps:get(ServerId, SrvZoneMap, 0),
	RoleList = maps:get(ZoneId, RoleMap, []),
	case lists:keyfind(RoleId, #role_info.role_id, RoleList) of
		#role_info{is_hosting = ?IN_HOSTING} -> true;
		_-> false
	end.

%% 是否已经死亡
is_die(RoleId, RoleMap) ->
	case maps:get(RoleId, RoleMap, []) of
		#role_info{is_die = ?IN_DIE_YES} -> true;
		_-> false
	end.

%% 场景回调
change_scene_callback(Player, _OldSceneInfo, OldSceneData, NewSceneData) ->
	#player_status{id = RoleId} = Player,
	ServerId = config:get_server_id(),
	Node = clusters_node:get_node(),
	case is_wasteland_scene(OldSceneData#ets_scene.id) of
		true  ->
			case is_wasteland_scene(NewSceneData#ets_scene.id) of
				true  ->
					ignore;
				false ->
					% Player1 = lib_scene:change_scene(Player, 0, 0, 0, [])
					% Player1 = lib_player:break_action_lock(Player, ?ERRCODE(err415_in_madness_land))
					case get_act_cls_type() of
						1 -> clusters_node:apply_cast(mod_wasteland_war, quit_scene, []);
						_ -> mod_wasteland_war:quit_scene(Node, ServerId, RoleId)
					end
			end,
			{ok, Player};
		false ->
			{ok, Player}
	end.

%% 获取血量上限
get_hp_lim(RoleId, RoleMap) ->
	case maps:get(RoleId, RoleMap, []) of
		#role_info{hp_lim = HpLim} -> true;
		_ -> HpLim = 0
	end,
	HpLim.

%% -------------------------------------------------------------------
%% @doc 托管相关
%% -------------------------------------------------------------------
start_hosting(SrvZoneMap) ->
	case config:get_cls_type() of
		1 ->
			ServerIds  = maps:keys(SrvZoneMap),
			F = fun(ServerId) ->
				% clusters_center:apply_cast(ServerId, offline_hosting_wasteland_war, wasteland_war_hosting, [])
				clusters_center:apply_cast(ServerId, ?MODULE, wasteland_war_hosting_local, [])
			    end,
			lists:foreach(F, ServerIds);
		_ ->
			offline_hosting_wasteland_war:wasteland_war_hosting()
	end.

wasteland_war_hosting_local() ->
	%% 已经进入跨服玩法才由跨服控制
	case lib_wasteland_war:get_act_cls_type() of
		1 -> offline_hosting_wasteland_war:wasteland_war_hosting();
		_ -> ignore
	end,
	ok.

%% -----------------------------------------------------------------------
%% @doc 结算相关
%% -----------------------------------------------------------------------
settle_reward_and_finish_war(ZoneId, Pools, FinishRound, RoleMap) ->  %% TODO:待优化
	NowTime  = utime:unixtime(),
	OldRoundScene = get_round_scene(FinishRound),
	%% 发放奖励
	AwardFun = fun(PoolId, TRoleMap) ->
		WarRoles  = lib_wasteland_war:proc_get_scene_roles(PoolId),
		%% 这里还是进行多一次排序
		RankWarRoles = sort_war_role_rank(WarRoles),
		TRoleMap1 = sort_role_rank(RankWarRoles, TRoleMap),
		%% 发放排行奖励
		% RoleList1 = send_rank_reward(RankWarRoles, TRoleList, 0),
		%% 发放轮数奖励
		% RoleList2 = send_round_reward(FinishRound, RankWarRoles, RoleList1),
		%% 最后一轮所有胜利者放到退出列表,延时发放奖励
		%% 推送活动结束
		case lists:keyfind(1, #war_field_role.war_rank, RankWarRoles) of
			#war_field_role{role_id = RoleId, war_score = WarScore} ->
				case maps:get(RoleId, TRoleMap1, []) of
					#role_info{server_num = ServerNum, role_name = RoleName, war_field = _WarField} ->
						lib_wasteland_war:notice_act_end(ZoneId, ServerNum, RoleName, WarScore);
					_ ->
						ignore
				end;
			_ ->
				ignore
		end,
		lib_wasteland_war:proc_put_exit_roles(PoolId, RankWarRoles),
		%% 强制变为和平状态
		F = fun(#war_field_role{role_id = TRoleId, node = TNode, mon_id = TMonId}) ->
			#role_info{is_die = IsDie, is_hosting = IsHosting, hp_lim = HpLim, is_in_scene = IsInScene} = maps:get(TRoleId, TRoleMap1, []),
			IsInScene == ?IN_SCENE_YES andalso notice_out(TNode, TRoleId, 1, NowTime, TMonId),
			IsInScene == ?IN_SCENE_YES andalso change_pk_status(TNode, TRoleId, IsHosting, OldRoundScene, PoolId, TMonId),
			(IsInScene == ?IN_SCENE_YES andalso IsDie == ?IN_DIE_YES) andalso revive_player(TNode, TRoleId, IsHosting, OldRoundScene, PoolId, TMonId, HpLim)
		end,
		lists:map(F, RankWarRoles),
		%% 清除伤害列表
		clean_hurt_list(RankWarRoles),
		TRoleMap1
	end,
	RoleMap1 = lists:foldl(AwardFun, RoleMap, Pools),
	RoleMap1.

settle_reward_and_take_next_round(Pools, OldRound, RoleMap) -> %% TODO:待优化
	NextRound   = OldRound + 1,
	NowTime     = utime:unixtime(),
	OldRoundScene = get_round_scene(OldRound),
	%% 计算下一轮次可进入排名
	CanEnterNextRank =
		case data_wasteland_war:get_round_cfg(NextRound) of
			#wasteland_round_reward_cfg{max_role = MaxRole} -> MaxRole;
			_ -> ?DEFAULT_ROLE_NUM
		end,
	ForFun = fun(PoolId, TRoleMap) ->
		WarRoles     = lib_wasteland_war:proc_get_scene_roles(PoolId),
		RankWarRoles = sort_war_role_rank(WarRoles),
		TRoleMap1 = sort_role_rank(RankWarRoles, TRoleMap),
		%% 发放奖励 & 更新托管玩家奖励（按分战场发放）
		% RoleList1 = send_rank_reward(RankWarRoles, TRoleList, CanEnterNextRank),
		%% 更新分线战场玩家数据
		{EnterNextRoles, ExitsRoles} = split_rank_role(RankWarRoles, CanEnterNextRank),
		proc_put_scene_roles(PoolId, EnterNextRoles),
		%% 淘汰者延时发放奖励
		proc_put_exit_roles(PoolId, ExitsRoles),
		%% 强制变为和平状态,推送退出时间
		ExitFun = fun(#war_field_role{role_id = TRoleId, node = TNode, mon_id = TMonId}) ->
			#role_info{is_die = IsDie, is_hosting = IsHosting, hp_lim = HpLim, is_in_scene = IsInScene} = maps:get(TRoleId, TRoleMap1, []),
			IsInScene == ?IN_SCENE_YES andalso notice_out(TNode, TRoleId, 2, NowTime+15, TMonId),
			IsInScene == ?IN_SCENE_YES andalso change_pk_status(TNode, TRoleId, IsHosting, OldRoundScene, PoolId, TMonId),
			(IsInScene == ?IN_SCENE_YES andalso IsDie == ?IN_DIE_YES) andalso revive_player(TNode, TRoleId, IsHosting, OldRoundScene, PoolId, TMonId, HpLim)
		end,
		lists:map(ExitFun, ExitsRoles),
		%% 清除伤害列表(只处理退出的)
		clean_hurt_list(ExitsRoles),
		%% 此时晋级玩家暂时变成和平状态,不可击杀其他玩家(有两秒的晋级时间)
		NextFun = fun(#war_field_role{role_id = TRoleId1, node = TNode1}) ->
			#role_info{is_hosting = IsHosting1, mon_id = TMonId1, is_in_scene = IsInScene1} = maps:get(TRoleId1, TRoleMap1, []),
			IsInScene1 == ?IN_SCENE_YES andalso notice_out(TNode1, TRoleId1, 1, NowTime, TMonId1),
			IsInScene1 == ?IN_SCENE_YES andalso change_pk_status(TNode1, TRoleId1, IsHosting1, OldRoundScene, PoolId, TMonId1)
		end,
		lists:map(NextFun, EnterNextRoles),
		TRoleMap1
	end,
	RoleMap1 = lists:foldl(ForFun, RoleMap, Pools),
	RoleMap1.

%% 积分排行
%% @param:
%%      WarRoles - 还在战场里面的玩家
%%      RoleList - 整个玩法的玩家
%% return:
%%      [#war_field_role{}...] / [#role_info{}...]
sort_war_role_rank(WarRoles) ->
	SortFun = fun(#war_field_role{war_score = ScoreA}, #war_field_role{war_score = ScoreB}) ->
		case ScoreA >= ScoreB of
			true -> true;
			_ -> false
		end end,
	SortRoles  = lists:sort(SortFun, WarRoles),
	RankFun = fun(#war_field_role{role_id = RoleId} = WarFieldRole, {Rank, TRankList}) ->
		{Rank + 1, lists:keystore(RoleId, #war_field_role.role_id, TRankList, WarFieldRole#war_field_role{war_rank = Rank})}
	end,
	{_, RankRoles}  = lists:foldl(RankFun, {1, []}, SortRoles),
	% ?INFO_TTY("WarRankRoles is ~w~n",[RankRoles]),
	RankRoles.

sort_role_rank(SortWarRoles, RoleMap) ->
	RankFun = fun(#war_field_role{role_id = RoleId, war_rank = Rank, war_score = WarScore} = _WarFieldRole, TRankMap) ->
					case maps:get(RoleId, TRankMap, []) of
						#role_info{} = RoleInfo ->
							maps:put(RoleId, RoleInfo#role_info{rank = Rank, score = WarScore}, TRankMap);
						_ ->
							TRankMap
					end
	          end,
	RankRoles  = lists:foldl(RankFun, RoleMap, SortWarRoles),
	% ?INFO_TTY("RankRoles is ~w~n",[RankRoles]),
	RankRoles.

%% 积分和排行奖励
send_rank_reward(RankRoles, RoleMap) ->
	F = fun(#war_field_role{role_id = RoleId, war_score = Score, war_rank = Rank}, {TSendRoleList, TRoleMap}) ->
		case data_wasteland_war:get_rank_reward(Rank) of
			[_|_] = RankReward -> skip;
			_ -> RankReward = []
		end,
		case maps:get(RoleId, TRoleMap, []) of
			#role_info{is_hosting = ?IN_HOSTING, host_reward = HostReward, node = Node} = RoleInfo ->
				%% 托管玩家奖励
				AllReward1 = lib_goods_util:merge_same_goods(RankReward++HostReward),
				NewRoleInfo = RoleInfo#role_info{host_reward = AllReward1, is_over = ?IS_OVER_YES},
				TSendRoleList1 = lists:keystore(RoleId, #role_info.role_id, TSendRoleList, NewRoleInfo),
				%% 排行奖励是最后发放的奖励，需要记录托管日志
				unode:apply(Node, offline_hosting, host_behavior_reward, [RoleId, AllReward1]),
				TRoleMap1 = maps:put(RoleId, NewRoleInfo, TRoleMap),
				{TSendRoleList1, TRoleMap1};
			#role_info{node = Node, is_in_scene = IsInScene, round = Round} = RoleInfo ->
				%% 其他玩家直接发放(5s后强制退出场景)
				Title = utext:get(6020001),  Content = utext:get(6020002, [Score, Rank]),
				case RankReward =/= [] of
					true ->
						case IsInScene of
							1 ->
								Produce = #produce{type = wasteland_war, title = Title, content = Content, reward = RankReward},
								unode:apply(Node, lib_goods_api, send_reward_with_mail, [RoleId, Produce, ?MOD_WASTELAND_WAR]);
							_ ->
								unode:apply(Node, lib_mail_api, send_sys_mail, [[RoleId], ?MOD_WASTELAND_WAR, Title, Content, RankReward])
						end;
					_ ->
						ignore
				end,
				NewRoleInfo = RoleInfo#role_info{is_over = ?IS_OVER_YES},
				% Args = [RoleId, 0, 0, 0, []],
				% IsInScene == 1 andalso lib_wasteland_war:switch_scene_callback(Node, Args),
				%% 存储最新轮数(不管玩家在不在场)
				% db_update_rank_round(Round, ServerId, RoleId),
				%% 推送结算(在场景内才推送)
				AllRound = length(data_wasteland_war:get_all_scene()), %% 最大轮数与场景数相同
				case Round < AllRound of
					true when IsInScene == 1->
						send_to_client(Node, RoleId, 60209, [2, Rank, Score]);
					_ when IsInScene == 1->
						send_to_client(Node, RoleId, 60209, [1, Rank, Score]);
					_ ->
						ignore
				end,
				TRoleMap1 = maps:put(RoleId, NewRoleInfo, TRoleMap),
				{TSendRoleList, TRoleMap1};
			_ ->
				{TSendRoleList, TRoleMap}
		end
	end,
	{SendRoleList, NewRoleMap} = lists:foldl(F, {[], RoleMap}, RankRoles),
	%% 给被踢出假人发放奖励
	spawn(fun() -> send_reward_to_hosting_role(SendRoleList) end),
	NewRoleMap.

%% 发放积分奖励(积分变更时发放,需要检测是否已经发放过上一积分段奖励)
%% 因为积分变更时玩家大多情况在场景内,除了托管玩家,因此不做是否在场景判断
send_score_reward(KillId, NewWarScore, RoleMap) ->
	case data_wasteland_war:get_score_reward_cfg(NewWarScore) of
		#wasteland_score_reward_cfg{need_score = NeedScore, reward = ScoreReward} -> skip;
		_ -> NeedScore = 0, ScoreReward = []
	end,
	case NeedScore of
		0 -> RoleMap;
		_ ->
			NewRoleMap =
			case maps:get(KillId, RoleMap, []) of
				#role_info{is_hosting = ?IN_HOSTING, host_reward = HostReward, reward_score = ScoreList} = RoleInfo ->
					case lists:member(NeedScore, ScoreList) of
						false ->
							%% 托管玩家奖励先存起来
							AllReward1 = lib_goods_util:merge_same_goods(ScoreReward++HostReward),
							NewRoleInfo = RoleInfo#role_info{host_reward = AllReward1, reward_score = [NeedScore|lists:delete(NeedScore, ScoreList)]},
							maps:put(KillId, NewRoleInfo, RoleMap);
						_ ->
							RoleMap
					end;
				#role_info{node = Node, reward_score = ScoreList} = RoleInfo ->
					Produce = #produce{type = wasteland_war, reward = ScoreReward},
					case lists:member(NeedScore, ScoreList) of
						false ->
							ScoreReward =/= [] andalso unode:apply(Node, lib_goods_api, send_reward_with_mail, [KillId, Produce, ?MOD_WASTELAND_WAR]),
							maps:put(KillId, RoleInfo#role_info{reward_score = [NeedScore|lists:delete(NeedScore, ScoreList)]}, RoleMap);
						_ ->
							RoleMap
					end
			end,
			NewRoleMap
	end.


%% 给晋级者(包括第一轮)发放轮数奖励,切换场景之后发放本轮奖励(需要兼容跨服)
send_round_reward(Round, RankRoles, RoleMap) ->
	AllRound = length(data_wasteland_war:get_all_scene()), %% 最大轮数与场景数相同
	NewRound = min(AllRound, Round),
	Reward = case data_wasteland_war:get_round_cfg(Round) of
		         #wasteland_round_reward_cfg{reward = TReward} -> TReward;
		         _ -> []
	         end,
	Title = utext:get(6020007), Content = utext:get(6020008),
	Produce = #produce{type = wasteland_war, reward = Reward, title = Title, content = Content},
	%% 非托管玩家发奖
	F0 = fun(#war_field_role{role_id = TRoleId}) ->
		case maps:get(TRoleId, RoleMap, []) of
			#role_info{node = Node, is_hosting = ?IN_HOSTING_NOT, is_in_scene = IsInScene} ->
				case IsInScene of
					?IN_SCENE_YES -> unode:apply(Node, lib_goods_api, send_reward_with_mail, [TRoleId, Produce]);
					_ -> unode:apply(Node, lib_mail_api, send_sys_mail, [[TRoleId], ?MOD_WASTELAND_WAR, 1, Title, Content, Reward])
				end,
				%% 错开时间
				timer:sleep(50);
			_ ->
				ignore
		end end,
	spawn(fun() -> lists:map(F0, RankRoles) end),
	F = fun(#war_field_role{role_id = RoleId}, TRoleMap) ->
		case maps:get(RoleId, RoleMap, []) of
			#role_info{is_hosting = ?IN_HOSTING_NOT} = RoleInfo ->
				% ?ERR("Reward is ~w~n",[Reward]),
				maps:put(RoleId, RoleInfo#role_info{round = NewRound}, TRoleMap);
			#role_info{host_reward = HostReward} = RoleInfo ->
				% ?ERR("HostReward is ~w~n",[HostReward]),
				AllReward = lib_goods_util:merge_same_goods(Reward++HostReward),
				maps:put(RoleId, RoleInfo#role_info{host_reward = AllReward, round = NewRound}, TRoleMap);
			_ ->
				TRoleMap
		end
	end,
	NewRoleMap = lists:foldl(F, RoleMap, RankRoles),
	NewRoleMap.

%% 发放奖励给托管玩家并记录日志
send_reward_to_hosting_role(RoleList) ->
	F = fun(#role_info{role_id = RoleId, node = Node, is_hosting = IsHosting, host_reward = Reward})
		when IsHosting == ?IN_HOSTING, Reward =/= [] ->
			Title = utext:get(6020005),  Content = utext:get(6020006),
			unode:apply(Node, lib_mail_api, send_sys_mail, [[RoleId], Title, Content, Reward]),
			%% 错开时间
			timer:sleep(50);
		(_) ->
			ignore
	    end,
	lists:foreach(F, RoleList).

%% 分离可以进入下一轮和不可以进入下一轮玩家
split_rank_role(RankRoles, CanEnterNextRank) ->
	F = fun(#war_field_role{role_id = RoleId, war_rank = Rank} = WarFieldRole, {CanEnter, CanNotEnter}) when Rank =< CanEnterNextRank, Rank > 0 ->
		{lists:keystore(RoleId, #war_field_role.role_id, CanEnter, WarFieldRole), CanNotEnter};
		(#war_field_role{role_id = RoleId} = WarFieldRole, {CanEnter, CanNotEnter}) ->
			{CanEnter, lists:keystore(RoleId, #war_field_role.role_id, CanNotEnter, WarFieldRole)}
	    end,
	lists:foldl(F, {[], []}, RankRoles).

%% -----------------------------------------------------------------------
%% @doc 玩法相关
%% -----------------------------------------------------------------------
create_crystal(Pools, Round, CrystalNumMap) ->
	F = fun(ScenePoolId, TCrystalNumMap) ->
		SceneId = get_round_scene(Round),
		StillNum = maps:get(ScenePoolId, TCrystalNumMap, 0),
		case data_wasteland_war:get_round_cfg(Round) of
			#wasteland_round_reward_cfg{max_crystal = CfgNum} -> skip;
			_ -> CfgNum = 0
		end,
		CreateNum = max(CfgNum - StillNum, 0),
		MaxCreateNum = data_wasteland_war:get_kv_cfg(crystal_num),
		case CreateNum =/= CfgNum of
			true -> CreateNum1 = min(CreateNum, MaxCreateNum);
			_ ->    CreateNum1 = CreateNum
		end,
		CollectHandler = {?MODULE, when_crystal_die, []},
		do_create_crystal(CreateNum1, 0, SceneId, ScenePoolId, CollectHandler),
		NewNum = CreateNum1+StillNum,
		%% 推送最新数量
		{ok, BinData0} = pt_602:write(60203, [NewNum]),
		{ok, BinData1} = pt_602:write(60207, [?SUCCESS]),
		%% 传闻
		TvBinData = lib_chat:make_tv(?MOD_WASTELAND_WAR, 8, []),
		BinData = <<BinData0/binary, BinData1/binary, TvBinData/binary>>,
		lib_server_send:send_to_scene(SceneId, ScenePoolId, BinData),
		maps:put(ScenePoolId, NewNum, TCrystalNumMap)
	end,
	lists:foldl(F, CrystalNumMap, Pools).

do_create_crystal(CreateNum, CreateNum, _SceneId, _ScenePoolId, _CollectHandler) -> ok;
do_create_crystal(CreateNum, NowNum, SceneId, ScenePoolId, CollectHandler) ->
	{X, Y} =
	case data_wasteland_war:get_pos_list(SceneId) of
		#wasteland_select_pos_cfg{pos_list = [{CenterX, CenterY, Radius}]} ->
			umath:rand_point_in_circle(CenterX, CenterY, Radius, 1);
		#wasteland_select_pos_cfg{pos_list = [_|_] = PosList} ->
			{CenterX, CenterY, _Radius} = urand:list_rand(PosList),
			{CenterX, CenterY};
		_ -> {0, 0}
	end,
	lib_mon:async_create_mon(?CRYSTAL_MON_ID, SceneId, ScenePoolId, X, Y, 0, 0, 1, [{collected_handler, CollectHandler}]),
	do_create_crystal(CreateNum, NowNum+1, SceneId, ScenePoolId, CollectHandler).

when_crystal_die(#scene_object{} = _Minfo, CollectorServerId, CollectorNode, CollectorId, _CollectArg) ->
	%% 击杀者积分
	mod_wasteland_war:kill_crystal(CollectorNode, CollectorId, CollectorServerId),
	ok.

%% 计算击杀者单次击杀获得积分
calc_killer_obtain_score(RoleMap, Killer, BeKiller) ->
	case Killer of
		#role_info{role_id = KillId, be_kill_num = BeKillNum, combo = Combo, max_combo = MaxCombo, kill_num = KillNum} ->
			%% 当被杀数为0,证明上一回合是击杀者,计算连杀
			case BeKillNum == 0 of
				true ->
					NewCombo    = Combo + 1,
					NewMaxCombo = max(NewCombo, MaxCombo);
				_ ->
					NewCombo    = 1,
					NewMaxCombo = max(NewCombo, MaxCombo)
			end,
			NewKiller = Killer#role_info{be_kill_num = 0, combo = NewCombo, max_combo = NewMaxCombo, kill_num = KillNum + 1},
			BaseScore = data_wasteland_war:get_kv_cfg(people_kill_grade),
			%% 连续击杀积分(击杀怪 + 人积分)
			ComboScore =
				case data_wasteland_war:get_bleed_score_cfg(NewCombo) of
					#wasteland_bleed_score_cfg{kill_score = CfgScore} -> CfgScore;
					_-> 0
				end,
			%% 终结积分
			EndScore =
				case BeKiller of
					#role_info{combo = BeKillCombo} when BeKillCombo > 1 ->
						case data_wasteland_war:get_bleed_score_cfg(BeKillCombo) of
							#wasteland_bleed_score_cfg{over_score = OverScore} -> OverScore;
							_ -> 0
						end;
					_ ->
						0
				end,
			{BaseScore + ComboScore + EndScore, maps:put(KillId, NewKiller, RoleMap)};
		_ ->
			{0, RoleMap}
	end.

%% 杀怪增加连斩(已废弃)
kill_crystal_add_combo(RoleList, Killer) ->
	case Killer of
		#role_info{role_id = KillId, be_kill_num = BeKillNum, combo = Combo, max_combo = MaxCombo, kill_num = KillNum} ->
			%% 当被杀数为0,证明上一回合是击杀者,计算连杀
			case BeKillNum == 0 of
				true ->
					NewCombo    = Combo + 1,
					NewMaxCombo = max(NewCombo, MaxCombo);
				_ ->
					NewCombo    = 1,
					NewMaxCombo = max(NewCombo, MaxCombo)
			end,
			%% 连续击杀积分(只计算计算击杀玩家的数量)
			ComboScore =
				case data_wasteland_war:get_bleed_score_cfg(NewCombo) of
					#wasteland_bleed_score_cfg{kill_score = CfgScore} -> CfgScore;
					_-> 0
				end,
			NewKiller = Killer#role_info{be_kill_num = 0, combo = NewCombo,
				max_combo = NewMaxCombo, kill_num = KillNum + 1},
			{ComboScore, lists:keystore(KillId, #role_info.role_id, RoleList, NewKiller)};
		_ ->
			{0, RoleList}
	end.

%% 被击杀者打断连杀(N连杀以上才通知,返回true,其他情况返回false)
check_stop_combo(BeKiller) ->
	case BeKiller of
		#role_info{be_kill_num = BeKillNum, combo = Combo} ->
			case Combo >= ?SHUTDOWN_TIMES of
				true ->
					{true, BeKiller#role_info{be_kill_num = 1, combo = 0}};
				false when Combo > 0 ->
					{false, BeKiller#role_info{be_kill_num = 1, combo = 0}};
				_ ->
					{false, BeKiller#role_info{be_kill_num = BeKillNum + 1, combo = 0}}
			end;
		_ ->
			{false, BeKiller}
	end.

%% 击杀者是否连杀
check_kill_combo(Killer) ->
	case Killer of
		#role_info{combo = Combo} ->
			%% 大于一次算连杀
			case Combo > 1 of
				true ->
					{true, Combo};
				_ ->
					{false, Combo}
			end;
		_ ->
			{false, 0}
	end.

%% 伤害触发 玩家进程
%% 备注:此处收集伤害列表，死亡时更新
player_be_hurted(Player, Atter) ->
	#player_status{id = BeHurtId, scene = SceneId} = Player,
	#battle_return_atter{id = HurtId} = Atter,
	case lib_wasteland_war:is_wasteland_scene(SceneId) of
		true ->
			HurtList  = proc_get_hurt_list(BeHurtId),
			HurtList1 = [{HurtId, utime:unixtime()}|lists:keydelete(HurtId, 1, HurtList)],
			SortHurtList = lists:reverse(lists:keysort(2, HurtList1)),
			%% 记录最近5个伤害玩家
			{NewHurtList, _} = ulists:split(5, SortHurtList),
			proc_put_hurt_list(BeHurtId, NewHurtList);
		_ ->
			ignore
	end,
	ok.

clean_hurt_list(WarRoles) ->
	ForFun = fun(WarRole, TMap) ->
		#war_field_role{role_id = RoleId, node = Node, mon_id = MonId} = WarRole,
		case MonId =< 0 of
			true ->
				case maps:get(Node, TMap, []) of
					[_|_] = RoleList -> maps:put(Node, [RoleId|RoleList], TMap);
					_ -> maps:put(Node, [RoleId], TMap)
				end;
			_ -> TMap
		end end,
	NodeRoleMap = lists:foldl(ForFun, #{}, WarRoles),
	CleanF = fun(TNode, TRoleLIst) ->
		unode:apply(TNode, ?MODULE, clean_hurt_list_on_node, [TRoleLIst])
	end,
	maps:map(CleanF, NodeRoleMap),
	ok.

clean_hurt_list_on_node(RoleList) ->
	CleanF = fun(RoleId) ->
		lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, clean_hurt_on_player, [])
	end,
	lists:map(CleanF, RoleList),
	ok.

clean_hurt_on_player(Player) ->
	#player_status{id = RoleId} = Player,
	HurtList = [],
	proc_put_hurt_list(RoleId, HurtList),
	ok.

%% 击杀玩家(被击杀者玩家进程)
kill_player(Node, SceneId, RoleId, RoleSrvId, KillId) ->
	case is_wasteland_scene(SceneId) of
		true ->
			HurtList = proc_get_hurt_list(RoleId),
			HurtList1 = case HurtList of
				            [] -> [{KillId, utime:unixtime()}];
				            _ -> HurtList
			            end,
			case HurtList1 =/= [] of
				true ->
					mod_wasteland_war:kill_player(Node, SceneId, KillId, RoleId, RoleSrvId, HurtList);
				_ -> ignore
			end;
		_ -> ignore
	end,
	ok.

add_assist_score(PoolId, KillId, BeKillId, HurtList, RoleMap) ->
	SceneRoles = proc_get_scene_roles(PoolId),
	case lists:keyfind(BeKillId, #war_field_role.role_id, SceneRoles) of
		#war_field_role{node = Node} ->
			HurtRoleList = [RoleId||{RoleId, _Time}<-HurtList, RoleId=/=KillId],  %% 击杀者排除在外
			%% 清空伤害列表
			Args = [BeKillId, ?APPLY_CAST_STATUS, ?MODULE, clean_hurt_on_player, []],
			unode:apply(Node, lib_player, apply_cast, Args),
			GetScore =
			case data_wasteland_war:get_kv_cfg(people_assist_grade) of
				Score when is_integer(Score) andalso Score > 0 -> Score;
				_ -> 0
			end,
			ForF = fun(TRoleId, {TSceneRoles, TRoleMap}) ->
				case lists:keyfind(TRoleId, #war_field_role.role_id, TSceneRoles) of
					#war_field_role{war_score = WarScore} = TWarRole ->
						NewWarScore = WarScore + GetScore,
						TSceneRoles1 = lists:keystore(TRoleId, #war_field_role.role_id, TSceneRoles,
							TWarRole#war_field_role{war_score = NewWarScore}),
						case maps:get(TRoleId, TRoleMap, []) of
							#role_info{score = OldScore} = TRoleInfo ->
								TRoleMap1 = maps:put(TRoleId, TRoleInfo#role_info{score = OldScore + GetScore},
									TRoleMap);
							_ ->
								TRoleMap1 = TRoleMap
						end,
						%% 记录TA数据
						pro_add_kill_role_score(TRoleId, GetScore),
						{TSceneRoles1, TRoleMap1};
					_ ->
						{TSceneRoles, TRoleMap}
				end end,
			{NewSceneRoles, NewRoleMap} = lists:foldl(ForF, {SceneRoles, RoleMap}, HurtRoleList),
			proc_put_scene_roles(PoolId, NewSceneRoles),
			NewRoleMap;
		_ ->
			RoleMap
	end.

%% 推送连杀(个人可看)
notice_kill_combo(Node, RoleId, Combo, MonId) ->
	MonId =< 0 andalso send_to_client(Node, RoleId, 60210, [Combo]).
	% lib_server_send:send_to_scene(SceneId, PoolId, BinData).

%% 推送终结(场景内可看)
notice_stop_kill(PoolId, Killer, BeKiller) ->
	#role_info{figure = KillFigure}   = Killer,
	#role_info{figure = BeKillFigure} = BeKiller,
	WarRoles  = lib_wasteland_war:proc_get_scene_roles(PoolId),
	RoleIds   = [{RoleId, Node, MonId}||#war_field_role{role_id = RoleId, node = Node, mon_id = MonId}<-WarRoles],
	ForFun = fun({TRoleId, TNode, TMonId}) ->
		TMonId =< 0 andalso send_to_client(TNode, TRoleId, 60208, [KillFigure, BeKillFigure])
	end,
	lists:foreach(ForFun, RoleIds).

%% 推送最新积分排行(控制计算频率&网络流量每个分线定时更新)
notice_new_score_rank(_Round, _PoolId, WarSceneRoles, RoleMap) ->
	%% 推送最新积分
	F = fun(WarRole) ->
		#war_field_role{node = Node, role_id = RoleId, war_score = Score, war_rank = Rank, mon_id = MonId} = WarRole,
		case maps:get(RoleId, RoleMap, []) of
			#role_info{is_in_scene = 1} ->
				MonId =< 0 andalso lib_wasteland_war:send_to_client(Node, RoleId, 60204, [Score, Rank]);
			_ -> ignore
		end end,
	lists:foreach(F, WarSceneRoles).

get_war_field_send(PoolId) ->
	case get(?WAR_FIELD_SEND(PoolId)) of
		Time when is_integer(Time) -> Time;
		_ -> 0
	end.

put_war_field_send(PoolId) ->
	Time = utime:longunixtime(),
	put(?WAR_FIELD_SEND(PoolId), Time).

check_can_send(Round, PoolId) ->
	NowTime = utime:longunixtime(),
	OldTime = get_war_field_send(PoolId),
	GapTime = case Round of
				  1 -> 2000;
				  2 -> 1600;
				  3 -> 0;
				  _ -> 500
	          end,
	case NowTime >= OldTime + GapTime of
		true -> true;
		_ -> false
	end.

%% 重新计算排行
recalc_role_rank(KillId, GetScore, RoleMap, ZoneId, Round) ->
	%% 分区重新排名
	{_Type, NewRoleMap} =
		case maps:get(KillId, RoleMap, []) of
			%% 需要判断是否已经被淘汰,因为有延迟结算时间
			#role_info{war_field = WarFieldNum, score = Score, is_over = ?IS_OVER_NO} ->
				NewScore = Score + GetScore,
				%% 战区重新排名
				case ZoneId > 0 of
					true ->
						PoolId     = get_role_pool(ZoneId, WarFieldNum),
						SceneRoles = proc_get_scene_roles(PoolId),
						NewSceneRoles =
							case lists:keyfind(KillId, #war_field_role.role_id, SceneRoles) of
								#war_field_role{war_score = WarScore} = WarRole ->
									NewWarScore = WarScore + GetScore,
									lists:keystore(KillId, #war_field_role.role_id, SceneRoles, WarRole#war_field_role{war_score = NewWarScore});
								_ ->
									SceneRoles
							end,
						%% 对不同轮数和分线作延迟计算  % NewSceneRoles,
						case check_can_send(Round, PoolId) of
							true ->
								SortSceneRoles = sort_war_role_rank(NewSceneRoles),
								put_war_field_send(PoolId),
								%% 推送积分排行
								notice_new_score_rank(Round, PoolId, SortSceneRoles, RoleMap);
							_ ->
								SortSceneRoles = NewSceneRoles
						end,
						proc_put_scene_roles(PoolId, SortSceneRoles),
						%% 更积分 & 发放积分奖励
						RoleMap1    = send_score_reward(KillId, NewScore, RoleMap),
						NewRoleInfo = maps:get(KillId, RoleMap1, []),
						RoleMap2 = maps:put(KillId, NewRoleInfo#role_info{score = NewScore}, RoleMap1),
						%% 把分战场的排行赋值给角色排行
						SortRoleMap = sort_role_rank(SortSceneRoles, RoleMap2),
						{new, SortRoleMap};
					_ ->
						{old, RoleMap}
				end;
			_ ->
				{old, RoleMap}
		end,
	%% 存库,由于数据库数据只是用于展示,并不重要,放在活动结束时存储
	NewRoleMap.

%% 存储排行榜数据
save_role_list(RoleMap) ->
	RoleList = maps:values(RoleMap),
	NewSQLs = format_bit_string(RoleList),
	if
		NewSQLs =/= [] ->
			NewValues = util:join_bitstring(NewSQLs, <<", ">>),
			FinalSql  = util:fmt_bin(?SQL_BATCH_SAVE_WASTELAND_RANK, [NewValues]),
			db:execute(FinalSql);
		true -> skip
	end.

format_bit_string(RoleList) ->
	SaveFun = fun(#role_info{} = RoleInfo, TSqlList) ->
					#role_info{server_id = ServerId, role_id = RRoleId, role_name = RoleName,
						war_field = WarField, round = Round, rank = Rank, score = RScore,
						kill_num = KillNum, max_combo = MaxCombo, sex = Sex, vip_lv = VipLv,
						sup_vip = SupVip, server_num = ServerNum, power = Power
					} = RoleInfo,
					TSql = util:fmt_bin(?SQL_BATCH_SAVE_WASTELAND_RANK_VALUE, [ServerId, RRoleId, RoleName, WarField,
						Round, Rank, RScore, KillNum, MaxCombo, Sex, VipLv, SupVip, ServerNum, Power]),
					[TSql|TSqlList];
				(_, TSqlList) ->
					TSqlList
		end,
	NewSQLs = lists:foldl(SaveFun, [], RoleList),
	NewSQLs.

%% 清理场景玩家
clean_scene_player(RoleMap, _ZoneIds) when is_map(RoleMap) ->
	CleanF = fun(_, #role_info{node = Node, role_id = RoleId, is_in_scene = IsInScene, mon_id = MonId}) ->
		Args = [RoleId, 0, 0, 0, []],
		(IsInScene == 1 andalso MonId=< 0) andalso lib_wasteland_war:switch_scene_callback(Node, Args)
	    end,
	maps:map(CleanF, RoleMap);
clean_scene_player(_, _) ->
	ignore.

%% 结算期间强制变为和平状态
change_pk_status(Node, RoleId, IsHosting, RoundScene, PoolId, MonId) ->
	case IsHosting of
		?IN_HOSTING_NOT ->
			Args = [RoleId, ?APPLY_CAST_SAVE, lib_player, change_pkstatus, [?PK_PEACE, true]],
			unode:apply(Node, lib_player, apply_cast, Args);
		_ when MonId > 0 ->			
			%% 假人非攻击状态
			mon_mgr:send({RoundScene, PoolId}, MonId, 'sleep');
		_ ->
			ignore
	end,
	ok.

%% 结算期间如果死亡则拉活
revive_player(Node, RoleId, IsHosting, RoundScene, PoolId, MonId, HpLim) ->
	case IsHosting of
		?IN_HOSTING_NOT ->
			Args = [RoleId, ?APPLY_CAST_SAVE, ?MODULE, revive_player_on_node, [5]],
			unode:apply(Node, lib_player, apply_cast, Args);
		_ when MonId > 0 ->
			mon_mgr:send({RoundScene, PoolId}, MonId, {'change_attr', [{hp, max(20000, HpLim)}, {state, sleep}]});
		_ ->
			ignore
	end.

revive_player_on_node(Player, Type) ->
	case lib_revive:revive(Player, Type) of
		{_Result, Player1} -> {ok, Player1};
		_ -> {ok, Player}
	end.

%% 推送退出时间
notice_out(Node, RoleId, Type, ExitTime, MonId) ->
	case MonId > 0 of
		true -> ignore;
		_ -> unode:apply(Node, lib_server_send, send_to_uid, [RoleId, pt_602, 60211, [Type, ExitTime]])
	end,
	ok.

%% ------------------------------------------------------------------------
%% 进程字典相关操作
proc_put_all_scene_roles(ZoneId, WarFieldMap) when is_map(WarFieldMap) ->
	WarFields = maps:keys(WarFieldMap),
	F = fun(WarFieldNum) ->
		PoolId = ZoneId * ?SCENE_POOL_OFFSET + WarFieldNum,
		SceneRoles = maps:get(WarFieldNum, WarFieldMap, []),
		proc_put_scene_roles(PoolId, SceneRoles)
	    end,
	lists:foreach(F, WarFields).

proc_put_scene_roles(PoolId, SceneRoles) ->
	RolesKey = ?WAR_FIELD_ROLE(PoolId),
	put(RolesKey, SceneRoles).

proc_get_scene_roles(PoolId) ->
	RolesKey = ?WAR_FIELD_ROLE(PoolId),
	case get(RolesKey) of
		[_|_] = SceneRoles -> SceneRoles;
		_ -> []
	end.

%% 淘汰玩家
proc_put_exit_roles(PoolId, SceneRoles) ->
	RolesKey = ?WAR_FIELD_EXIT_ROLE(PoolId),
	put(RolesKey, SceneRoles).

proc_get_exit_roles(PoolId) ->
	RolesKey = ?WAR_FIELD_EXIT_ROLE(PoolId),
	case get(RolesKey) of
		[_|_] = SceneRoles -> SceneRoles;
		_ -> []
	end.

%% -------------------------------------------------------------------
send_to_client(Node, RoleId, BinData) ->
	unode:apply(Node, lib_server_send, send_to_uid, [RoleId, BinData]).
send_to_client(Node, RoleId, Cmd, Msg) when is_integer(RoleId)->
	{ok, BinData} = pt_602:write(Cmd, Msg),
	unode:apply(Node, lib_server_send, send_to_uid, [RoleId, BinData]);
send_to_client(Node, Sid, Cmd, Msg) ->
	{ok, BinData} = pt_602:write(Cmd, Msg),
	unode:apply(Node, lib_server_send, send_to_sid, [Sid, BinData]).

%% 活动开放 todo: 游戏服延后2s，等待跨服数据同步完毕（有其他更好的做法？）
act_start(SubAct) ->
	case config:get_cls_type() of
		1 ->
			mod_wasteland_war:act_start(SubAct);
		_ ->
			spawn(fun() -> timer:sleep(3000), mod_wasteland_war:act_start(SubAct) end)
	end.

%% 通知活动开始
notice_act_start(StartTime, IsClsOpen, SubAct) ->
	MFA = {
		?MODULE, notice_act_start_local, [StartTime, SubAct]
	},
	case config:get_cls_type() of
		1 ->
			cross_zone:apply_cast(czone, {cross_zone, proc_apply_to_zone, [all, MFA]});
		_ ->
			case IsClsOpen of
				true ->
					ignore;
				_ ->
					ActType = 1,
					{ok, BinData} = pt_602:write(60200, [?WASTELAND_READY, StartTime, ActType]),
					lib_chat:send_TV({all}, ?MOD_WASTELAND_WAR, 1, []),
					lib_server_send:send_to_all(BinData)
			end
	end.

%% 跨服通知本服活动开放(只有跨服活动期间才推送)
notice_act_start_local(StartTime, SubAct) ->
	case is_local_start(SubAct) of
		true ->
			%% 如果是未进入跨服模式，返回false
			case get_act_cls_type() of
				?WAR_TYPE_LOCAL -> ignore;
				_ ->
					ActType = 2,
					{ok, BinData} = pt_602:write(60200, [?WASTELAND_READY, StartTime, ActType]),
					lib_chat:send_TV({all}, ?MOD_WASTELAND_WAR, 1, []),
					lib_server_send:send_to_all(BinData)
			end;
		_ ->
			ignore
	end,
	ok.

%% 通知活动状态变更
notice_act_change(Stage, StartTime, IsClsOpen) ->
	MFA = {
		mod_wasteland_war, notice_act_change_local, [Stage, StartTime]
	},
	case config:get_cls_type() of
		1 ->
			cross_zone:apply_cast(czone, {cross_zone, proc_apply_to_zone, [all, MFA]});
		_ ->
			case IsClsOpen of
				true -> ignore;
				_  ->
					ActType = 1,
					{ok, BinData} = pt_602:write(60200, [Stage, StartTime, ActType]),
					lib_server_send:send_to_all(BinData)
			end
	end.

%% 通知活动状态变更
notice_act_change_local(Stage, StartTime) ->
	case get_act_cls_type() of
		?WAR_TYPE_LOCAL ->
			ignore;
		_ ->
			ActType = 2,
			{ok, BinData} = pt_602:write(60200, [Stage, StartTime, ActType]),
			lib_server_send:send_to_all(BinData)
	end,
	ok.

%% 活动结束传闻
notice_act_end(ZoneId, ServerNum, RoleName, WarScore) ->
	MFA = {
		mod_wasteland_war, notice_act_end_local, [ServerNum, RoleName, WarScore]
	},
	case config:get_cls_type() of
		1 ->
			cross_zone:apply_cast(czone, {cross_zone, proc_apply_to_zone, [ZoneId, MFA]});
		_ ->
			%% 延迟10s清理数据
			lib_activitycalen_api:activity_end_clean_record(?MOD_WASTELAND_WAR, 10),
			lib_chat:send_TV({all}, ?MOD_WASTELAND_WAR, 2, [ServerNum, RoleName, WarScore])
	end.

%% 活动结束传闻
notice_act_end_local(ServerNum, RoleName, WarField) ->
	case get_act_cls_type() of
		?WAR_TYPE_LOCAL -> ignore;
		_ ->
			%% 延迟10s清理数据
			lib_activitycalen_api:activity_end_clean_record(?MOD_WASTELAND_WAR, 10),
			lib_chat:send_TV({all}, ?MOD_WASTELAND_WAR, 2, [ServerNum, RoleName, WarField])
	end,
	ok.

%% 连杀传闻
notice_combo_tv(RoleName, RoleId, Combo, ZoneId, SceneId, PoolId) ->
	case tv_type(Combo) of
		false -> ignore;
		Type ->
			Args = {
				lib_chat, send_TV, [{scene, SceneId, PoolId}, ?MOD_WASTELAND_WAR, Type, [RoleName, RoleId]]
			},
			MFA = {
				mod_wasteland_war, notice_combo_tv_local, [Args]
			},
			case config:get_cls_type() of
				1 ->
					cross_zone:apply_cast(czone, {cross_zone, proc_apply_to_zone, [ZoneId, MFA]});
				_ ->
					lib_chat:send_TV({scene, SceneId, PoolId}, ?MOD_WASTELAND_WAR, Type, [RoleName, RoleId])
			end
	end,
	ok.

notice_combo_tv_local(MFA) ->
	{Mod, Fun, Args} = MFA,
	apply(Mod, Fun, Args),
	ok.

%% 终结传闻
notice_end_combo_tv(KillerName, KillId, BeKillerName, BeKillId, Combo, ZoneId, SceneId, PoolId) ->
	case Combo >= 10 of
		false -> ignore;
		_ ->
			Args = {
				lib_chat, send_TV, [{scene, SceneId, PoolId}, ?MOD_WASTELAND_WAR, 7, [KillerName, KillId, BeKillerName, BeKillId, Combo]]
			},
			MFA = {
				mod_wasteland_war, notice_end_combo_tv_local, [Args]
			},
			case config:get_cls_type() of
				1 ->
					cross_zone:apply_cast(czone, {cross_zone, proc_apply_to_zone, [ZoneId, MFA]});
				_ ->
					lib_chat:send_TV({scene, SceneId, PoolId}, ?MOD_WASTELAND_WAR, 7, [KillerName, BeKillerName, Combo])
			end
	end,
	ok.

notice_end_combo_tv_local(MFA) ->
	{Mod, Fun, Args} = MFA,
	apply(Mod, Fun, Args),
	ok.

tv_type(Combo) ->
	case Combo of
		10 -> 3;
		20 -> 4;
		50 -> 5;
		100 -> 6;
		_ -> false
	end.

%% 跨服活动时，需要判断本地是否满足开放条件
is_local_start(SubAct) ->
	%% 999 - gm开放的直接通过
	case SubAct of
		999 -> true;
		_ ->
			lib_activitycalen:check_mod_ac_start(?MOD_WASTELAND_WAR, 0, SubAct)
	end.
	% OpenDay = util:get_open_day(),
	% IdList = data_activitycalen:get_ac_sub(?MOD_WASTELAND_WAR, 0),
	% F = fun(SubAct, Ack) ->
	%	case data_activitycalen:get_ac(?MOD_WASTELAND_WAR, 0, SubAct) of
	%		#base_ac{open_day = [{StartDay, _EndDay}], node_type = 2} ->
	%			case OpenDay >= StartDay of
	%				true -> true;
	%				_ -> Ack
	%			end;
	%		_ -> Ack
	%	end end,
	% lists:foldl(F, false, IdList).

%% TA数据相关处理(存储在进程字典)
%% 1> 进入时间
pro_record_enter_time(RoleId) ->
	NowTime = utime:unixtime(),
	NewTaRoleData =
	case get(?PRO_TA_ROLE_DATA(RoleId)) of
		#pro_ta_role_data{} = TaRoleData->
			TaRoleData;
		_ ->
			TaRoleData = #pro_ta_role_data{
				role_id     = RoleId
				,enter_time = NowTime
			},
			TaRoleData
	end,
	put(?PRO_TA_ROLE_DATA(RoleId), NewTaRoleData),
	ok.

%% 2> 击杀角色积分
pro_add_kill_role_score(RoleId, Score) ->
	NewTaRoleData =
	case get(?PRO_TA_ROLE_DATA(RoleId)) of
		#pro_ta_role_data{kill_score = OldScore} = TaRoleData ->
			NewScore = OldScore + max(0, Score),
			TaRoleData#pro_ta_role_data{kill_score = NewScore};
		_ ->
			TaRoleData = #pro_ta_role_data{
				role_id     = RoleId
				,kill_score = max(0, Score)
			},
			TaRoleData
	end,
	put(?PRO_TA_ROLE_DATA(RoleId), NewTaRoleData),
	ok.

%% 3> 击杀结晶积分
pro_add_kill_crystal_score(RoleId, Score) ->
	NewTaRoleData =
	case get(?PRO_TA_ROLE_DATA(RoleId)) of
		#pro_ta_role_data{crystal_score = OldScore} = TaRoleData ->
			NewScore = OldScore + max(0, Score),
			TaRoleData#pro_ta_role_data{crystal_score = NewScore};
		_ ->
			TaRoleData = #pro_ta_role_data{
				role_id        = RoleId
				,crystal_score = max(0, Score)
			},
			TaRoleData
	end,
	put(?PRO_TA_ROLE_DATA(RoleId), NewTaRoleData),
	ok.

%% 4> 复活次数
pro_record_revive_times(RoleId) ->
	NewTaRoleData =
	case get(?PRO_TA_ROLE_DATA(RoleId)) of
		#pro_ta_role_data{revive_times = OldTimes} = TaRoleData ->
			NewTimes = OldTimes + 1,
			TaRoleData#pro_ta_role_data{revive_times = NewTimes};
		_ ->
			TaRoleData = #pro_ta_role_data{
				role_id       = RoleId
				,revive_times = 1
			},
			TaRoleData
	end,
	put(?PRO_TA_ROLE_DATA(RoleId), NewTaRoleData),
	ok.

%% 获取进程字典记录的数据
pro_get_role_ta_data(RoleId) ->
	case get(?PRO_TA_ROLE_DATA(RoleId)) of
		#pro_ta_role_data{} = TaRoleData ->
			#pro_ta_role_data{
				revive_times   = ReviveTimes
				,kill_score    = KillScore
				,crystal_score = CrystalScore
				,enter_time    = EnterTime
			} = TaRoleData;
		_ ->
			ReviveTimes   = 0
			,KillScore    = 0
			,CrystalScore = 0
			,EnterTime    = utime:unixtime()
	end,
	{ReviveTimes, KillScore, CrystalScore, EnterTime}.

%% -------------------------------------------------------------------
%% 玩家进程 伤害列表处理
proc_get_hurt_list(RoleId) ->
	HurtKey = ?WASTELAND_HURT_LIST(RoleId),
	case get(HurtKey) of
		HurtList when is_list(HurtList) -> ok;
		_ -> HurtList = []
	end,
	HurtList.

proc_put_hurt_list(RoleId, HurtList) when is_list(HurtList) ->
	HurtKey = ?WASTELAND_HURT_LIST(RoleId),
	put(HurtKey, HurtList);
proc_put_hurt_list(_RoleId, _HurtList) ->
	error.

%% -------------------------------------------------------------------
%% 重启获取排行榜数据
get_role_list(SrvZoneMap) ->
	ClsType = config:get_cls_type(),
	case db_get_rank() of
		[_|_] = InfoList ->
			make_list(InfoList, ClsType, SrvZoneMap, #{});
		_ ->
			#{}
	end.

make_list([], _ClsType, _SrvZoneMap, RoleMap) -> RoleMap;
make_list([Info|L], ClsType, SrvZoneMap, TMap) ->
	[ServerId, RoleId, RoleName, WarField, Round, Rank, Score, KillNum, MaxCombo, Sex, VipLv, SupVip, ServerNum, Power] = Info,
	RoleInfo = #role_info{
		role_id = RoleId
		,role_name = RoleName
		,war_field = WarField
		,round = Round
		,score = Score
		,rank  = Rank
		,kill_num = KillNum
		,max_combo = MaxCombo
		,sex = Sex
		,vip_lv = VipLv
		,sup_vip = SupVip
		,server_num = ServerNum
		,power = Power
		,is_in_scene = 0
	},
	case ClsType of
		1 -> ZoneId = maps:get(ServerId, SrvZoneMap, 0);
		_ -> ZoneId = 1
	end,
	NewRoleList =
	case maps:get(ZoneId, TMap, []) of
		[_|_] = RoleList -> lists:keystore(RoleId, #role_info.role_id, RoleList, RoleInfo);
		_ -> lists:keystore(RoleId, #role_info.role_id, [], RoleInfo)
	end,
	NewRoleMap = maps:put(ZoneId, NewRoleList, TMap),
	make_list(L, ClsType, SrvZoneMap, NewRoleMap).


%%db_save_rank(ServerId, RoleId, RoleName, WarField, Round, Rank, Score, KillNum, MaxCombo, Sex, VipLv, SupVip, ServerNum, Power) ->
%%	Sql = io_lib:format(?SQL_SAVE_WASTELAND_RANK, [ServerId, RoleId, RoleName, WarField, Round, Rank, Score, KillNum, MaxCombo, Sex, VipLv, SupVip, ServerNum, Power]),
%%	db:execute(Sql).

db_get_rank() ->
	Sql = io_lib:format(?SQL_SELECT_WASTELAND_RANK, []),
	db:get_all(Sql).

db_delete_rank() ->
	% SQL_DELETE_WASTELAND_RANK
	Sql = io_lib:format(?SQL_DELETE_WASTELAND_RANK, []),
	db:execute(Sql).

%%db_update_rank_combo(KillNum, MaxCombo,ServerId, RoleId) ->
%%	Sql = io_lib:format(?SQL_UPDATE_WASTELAND_RANK_COMBO, [KillNum, MaxCombo,ServerId, RoleId]),
%%	db:execute(Sql).

db_update_rank_round(Round, ServerId, RoleId) ->
	Sql = io_lib:format(?SQL_UPDATE_WASTELAND_RANK_ROUND, [Round, ServerId, RoleId]),
	db:execute(Sql).

db_update_rank_name(RoleName, ServerId, RoleId) ->
	Sql = io_lib:format(?SQL_UPDATE_WASTELAND_RANK_NAME, [RoleName, ServerId, RoleId]),
	db:execute(Sql).

db_update_rank_vip(VipLv, ServerId, RoleId) ->
	Sql = io_lib:format(?SQL_UPDATE_WASTELAND_RANK_VIP, [VipLv, ServerId, RoleId]),
	db:execute(Sql).

db_update_rank_sup_vip(SupVip, ServerId, RoleId) ->
	Sql = io_lib:format(?SQL_UPDATE_WASTELAND_RANK_SUP_VIP, [SupVip, ServerId, RoleId]),
	db:execute(Sql).

db_update_rank_power(Power, ServerId, RoleId) ->
	Sql = io_lib:format(?SQL_UPDATE_WASTELAND_RANK_POWER, [Power, ServerId, RoleId]),
	db:execute(Sql).
