%%%-------------------------------------------------------------------
%%% @doc    废土战场(饥饿游戏)-分战场管理进程(每一个分区一个管理进程)
%%%         负责管理每个分区的战场数据
%%% Module  : mod_wasteland_worker.erl
%%% Created : 2020-08-03
%%% @Author : tyl
%%% @end
%%%-------------------------------------------------------------------
-module(mod_wasteland_worker).

-include("common.hrl").
-include("server.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("czone.hrl").
-behaviour(gen_server).

%% API
-export([
	start_link/1, calc_war_field/1, create_crystal/2, next_round/2, take_next_round/2
	,sync_role_score_rank/1, close_worker/1, gm_end/2
	,enter_scene/1, quit_scene/1, reconnect/1, fetch_score_rank/1, host_login/1, create_dummy/1
	,notice_round/1, kill_player/1, kill_crystal/1, record_die_state/1, clean_die_state/1
	,power_change/1, figure_change/1, node_add_log/8, after_enter_scene_on_node/2, do_pull_role_on_node/2
	,update_ta_data/5, node_update_ta_data/8, update_ta_data_on_player/2
]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-record(worker_state, {
	 zone_id      = 0             %% 分区Id
	,pool_list    = []            %% 分线列表
	,crystal_num  = #{}           %% 结晶数量#{pool => Num}
	,role_map     = #{}           %% 角色列表#{role_id=>#role_info{}...}
	,dummy_ids    = []            %% 记录假人唯一Id
	,round        = 0             %% 轮数(由mod_wasteland_war写入,写日志时只读,其他情况不使用)
	,delay_reward_ref = []        %% 延时发放奖励定时器(淘汰者 & 最后一轮全员)
	,force_out_ref = []           %% 强制退出定时器
}).

%% -------------------------------------------------------------
%% @doc 战场控制逻辑 @end
%% -------------------------------------------------------------
%% 分战场计算
calc_war_field(WorkerPid) ->
	gen_server:cast(WorkerPid, {'calc_war_field'}).

%% 定时刷新结晶
create_crystal(WorkerPid, Round) ->
	gen_server:cast(WorkerPid, {'create_crystal', Round}).

%% 进入下一轮
next_round(WorkerPid, OldRound) ->
	gen_server:cast(WorkerPid, {'next_round', OldRound}).

%% 协议轮切换场景 & 发放奖励
take_next_round(WorkerPid, Round) ->
	gen_server:cast(WorkerPid, {'take_next_round', Round}).

%% 排行数据上报(暂时可能不需要) todo
sync_role_score_rank(WorkerPid) ->
	gen_server:cast(WorkerPid, {'sync_role_score_rank'}).

%% 活动结束,注销管理进程
close_worker(WorkerPid) ->
	gen_server:cast(WorkerPid, {'close_worker'}).

%% gm结束
gm_end(WorkerPid, Round) ->
	gen_server:cast(WorkerPid, {'gm_end', Round}).

%% -------------------------------------------------------------
%% @doc 玩家相关操作
%%      注意玩家操作相关函数的参数为列表形式
%% @end
%% -------------------------------------------------------------
%% 进入场景
enter_scene([WorkerPid, Stage, Round, Node, RoleId, Figure, ServerId, ServerNum, IsHosting, HpLim]) ->
	gen_server:cast(WorkerPid, {'enter_scene', Stage, Round, Node, RoleId, Figure, ServerId, ServerNum, IsHosting, HpLim}).

%% 退出场景
quit_scene([WorkerPid, RoleId]) ->
	gen_server:cast(WorkerPid, {'quit_scene', RoleId}).

%% 断线重连
reconnect([WorkerPid, Stage, Round, RoundEndTime, Node, RoleId]) ->
	gen_server:cast(WorkerPid, {'reconnect', Stage, Round, RoundEndTime, Node, RoleId}).

%% 托管玩家登录
host_login([WorkerPid, Round, RoleId]) ->
	gen_server:cast(WorkerPid, {'host_login', Round, RoleId}).

create_dummy([WorkerPid, Node, ServerId, RoleId]) ->
	gen_server:cast(WorkerPid, {'create_dummy', Node, ServerId, RoleId}).

%% 查询排行榜数据
fetch_score_rank([WorkerPid, Node, RoleId, ServerId]) ->
	gen_server:cast(WorkerPid, {'fetch_score_rank', Node, RoleId, ServerId}).

%% 击杀玩家
kill_player([WorkerPid, Round, KillId, BeKillId, HurtList]) ->
	gen_server:cast(WorkerPid, {'kill_player', Round, KillId, BeKillId, HurtList}).

%% 采集结晶
kill_crystal([WorkerPid, Round, KillId]) ->
	gen_server:cast(WorkerPid, {'kill_crystal', Round, KillId}).

%% 记录死亡状态
record_die_state([WorkerPid, RoleId]) ->
	gen_server:cast(WorkerPid, {'record_die_state', RoleId}).

%% 清除死亡状态
clean_die_state([WorkerPid, RoleId]) ->
	gen_server:cast(WorkerPid, {'clean_die_state', RoleId}).

%% 推送轮次
notice_round([WorkerPid, Stage, Round, RoundEndTime, Node, RoleId]) ->
	gen_server:cast(WorkerPid, {'notice_round', Stage, Round, RoundEndTime, Node, RoleId}).

%% 战力变更
power_change([WorkerPid, RoleId, ServerId, Power]) ->
	gen_server:cast(WorkerPid, {'power_change', RoleId, ServerId, Power}).

%% 形象改变
figure_change([WorkerPid, RoleId, ServerId, Type, ChangeValue]) ->
	gen_server:cast(WorkerPid, {'figure_change', RoleId, ServerId, Type, ChangeValue}).

%% ---------------------------------------------------------------------------
start_link(ZoneId) ->
	gen_server:start_link(?MODULE, [ZoneId], []).

%%cast(Msg) ->
%%	gen_server:cast(?MODULE, Msg).
%%
%%call(Msg) ->
%%	gen_server:call(?MODULE, Msg).
%%
%%info(Msg) ->
%%	erlang:send(?MODULE, Msg).

init([ZoneId]) ->
	process_flag(trap_exit, true),
	State = #worker_state{zone_id = ZoneId},
	{ok, State}.

handle_call(Msg, From, State) ->
	try do_handle_call(Msg, From, State) of
		Reply ->
			Reply
	catch ?EXCEPTION(Class, Reason, StackTrace) ->
		Error = {Class, Reason, StackTrace},
		?ERR("~w:handle_call error:~p~n", [?MODULE, Error]),
		{error, Reason}
	end.

handle_cast(Msg, State) ->
	try do_handle_cast(Msg, State) of
		Result ->
			Result
	catch ?EXCEPTION(Class, Reason, StackTrace) ->
		Error = {Class, Reason, StackTrace},
		?ERR("~w:handle_cast error:~p~n", [?MODULE, Error]),
		{noreply, State}
	end.

handle_info(Info, State) ->
	try do_handle_info(Info, State) of
		Result ->
			Result
	catch ?EXCEPTION(Class, Reason, StackTrace) ->
		Error = {Class, Reason, StackTrace},
		?ERR("~w:handle_info error:~p~n", [?MODULE, Error]),
		{noreply, State}
	end.

terminate(_Reason, _State) ->
	ok.

code_change(_OldVsn, State, _Extra) ->
	{ok, State}.

%% ---------------------------------------------------------------------------
do_handle_call(_Msg, _From, State) ->
	{reply, ok, State}.

%% ---------------------------------------------------------------------------
do_handle_cast({'enter_scene', Stage, Round, Node, RoleId, Figure, ServerId, ServerNum, IsHosting, HpLim}, State) ->
	#worker_state{zone_id = ZoneId, role_map = RoleMap, crystal_num = CrystalNumMap} = State,
	RoundScene     = lib_wasteland_war:get_round_scene(Round),
	{BornX, BornY} = lib_wasteland_war:get_rand_born_xy(RoundScene),
	case maps:get(RoleId, RoleMap, []) of
		%% 有数据，但可能是上次活动保留的(需要过滤为本次活动开放的数据) %% 理论上 Node = RoleNode
		#role_info{is_over = IsOver, node = RoleNode, war_field = WarFieldNum, is_hosting = IsInHost,
			rank = Rank, score = Score, ready_pool = ReadyPool} when Stage =/= ?WASTELAND_REST ->
			case IsInHost of
				1 ->
					%% 战斗开始时告诉玩家已经在托管中
					lib_wasteland_war:send_to_client(Node, RoleId, 60201, [?ERRCODE(err602_is_hosting)]),
					NewState = State;
				_ ->
					case IsOver of
						?IS_OVER_YES ->
							lib_wasteland_war:send_to_client(Node, RoleId, 60201, [?ERRCODE(err602_game_over)]),
							NewState = State;
						_ ->
							%% 战斗开启之后不能再进入
							case Stage of
								?WASTELAND_RUN ->
									lib_wasteland_war:send_to_client(Node, RoleId, 60201, [?ERRCODE(err602_in_act)]),
									NewState = State;
								?WASTELAND_REST -> % 理论上不会发送
									lib_wasteland_war:send_to_client(Node, RoleId, 60201, [?ERRCODE(err602_not_open)]),
									NewState = State;
								%% 如果曾经进入再退出,假人上线不作处理
								_ when IsHosting == ?IN_HOSTING ->
									NewState = State;
								_ ->
									ZoneId = lib_wasteland_war:get_server_zone(ServerId),
									AllocLine  = lib_wasteland_war:get_role_pool(ZoneId, WarFieldNum),
									AllocLine1 = case Round of
										             0 when ReadyPool =/= 0 -> ReadyPool;
										             _ -> AllocLine
									             end,
									Args = [RoleId, RoundScene, AllocLine1, 0, BornX, BornY, false, []],
									lib_wasteland_war:switch_scene_callback(RoleNode, Args),
									clusters_center:apply_cast(RoleNode, lib_activity_reserve, enter_act, [RoleId, ?MOD_WASTELAND_WAR]),
									NewRoleMap = case maps:get(RoleId, RoleMap, []) of
										             #role_info{} = RoleInfo ->
											             maps:put(RoleId, RoleInfo#role_info{is_in_scene = ?IN_SCENE_YES}, RoleMap);
										             _ ->
											             RoleMap
									             end,
									{ok, BinData0} = pt_602:write(60201, [?SUCCESS]),
									%% 推送结晶数
									CrystalNum     = maps:get(AllocLine, CrystalNumMap, 0),
									{ok, BinData1} = pt_602:write(60203, [CrystalNum]),
									%% 推送积分排行
									WarRoles = lib_wasteland_war:proc_get_scene_roles(AllocLine),
									case lists:keyfind(RoleId, #war_field_role.role_id, WarRoles) of
										#war_field_role{war_score = Score, war_rank = Rank, mon_id = MonId} ->
											case MonId =< 0 of
												true ->
													{ok, BinData2} = pt_602:write(60204, [Score, Rank]),
													BinData = <<BinData0/binary, BinData1/binary, BinData2/binary>>;
												_ ->
													BinData = <<BinData0/binary, BinData1/binary>>
											end;
										_ -> BinData = <<BinData0/binary, BinData1/binary>>
									end,
									lib_wasteland_war:send_to_client(Node, RoleId, BinData),
									%% 日志
									case IsHosting of
										0 -> add_log(Node, ZoneId, ServerId, ServerNum, RoleId, 1, Round, Rank, Score);
										_ -> add_log(Node, ZoneId, ServerId, ServerNum, RoleId, 2, Round, Rank, Score)
									end,
									NewState = State#worker_state{role_map = NewRoleMap}
							end
					end
			end;
		_ ->
			%% 没有玩家的相关数据(托管玩家不切换场景,只记录数据)
			case Stage of
				%% 只有在准备阶段才可以进入(准备阶段进入ZoneId*OFFSET分线)
				?WASTELAND_READY ->
					RoleNum = maps:size(RoleMap) + 1,
					PoolId = lib_wasteland_war:get_ready_pool(ZoneId) + RoleNum div 50,  %% 将玩家分散，每50人一个场景，避免聚集造成场景压力
					RoleInfo = lib_wasteland_war:trans_to_role(Node, RoleId, Figure, ServerId, ServerNum, IsHosting, HpLim, PoolId),
					%% 此处直接替换托管
					NewRoleMap = maps:put(RoleId, RoleInfo, RoleMap),
					DefaultSceneId = lib_wasteland_war:get_default_scene(),
					% ?INFO_TTY("RoleId, ?CLS_WAR_DEFAULT_SCENE, PoolId, 0, BornX, BornY, false, [] is ~w~n",[{RoleId, ?CLS_WAR_DEFAULT_SCENE, PoolId, 0, BornX, BornY, false, []}]),
					Args = [RoleId, DefaultSceneId, PoolId, 0, BornX, BornY, false, []], % {pk,{?PK_PEACE_ULTIMATE, true}}
					case IsHosting of
						?IN_HOSTING_NOT -> lib_wasteland_war:switch_scene_callback(Node, Args);
						_ -> unode:apply(Node, offline_hosting_wasteland_war, cast_create_dummy,
								[Node, ServerId, RoleId, DefaultSceneId, PoolId, BornX, BornY])
					end,
					IsHosting == ?IN_HOSTING_NOT andalso lib_wasteland_war:send_to_client(Node, RoleId, 60201, [?SUCCESS]),
					IsHosting == ?IN_HOSTING_NOT andalso clusters_center:apply_cast(Node, lib_activity_reserve, enter_act, [RoleId, ?MOD_WASTELAND_WAR]),
					%% 进入场景之后事件触发 & 日志
					LogMsg = [ZoneId, ServerId, ServerNum, Round],
					after_enter_scene(Node, RoleId, IsHosting, LogMsg),
					NewState = State#worker_state{role_map = NewRoleMap};
				?WASTELAND_REST -> % 理论上不会发送
					lib_wasteland_war:send_to_client(Node, RoleId, 60201, [?ERRCODE(err602_not_open)]),
					NewState = State;
				_ -> % 理论上不会发送
					lib_wasteland_war:send_to_client(Node, RoleId, 60201, [?ERRCODE(err602_in_act)]),
					NewState = State
			end
	end,
	{noreply, NewState};

do_handle_cast({'quit_scene', RoleId}, State) ->
	#worker_state{zone_id = ZoneId, role_map = RoleMap, round = Round} = State,
	case maps:get(RoleId, RoleMap, []) of
		#role_info{node = Node, war_field = WarFieldNum, server_id = ServerId,
			server_num = ServerNum, rank = Rank, score = Score} = RoleInfo ->
			Args = [RoleId, 0, 0, 0, []],
			lib_wasteland_war:switch_scene_callback(Node, Args),
			%% 清理仇恨列表
			PoolId = lib_wasteland_war:get_role_pool(ZoneId, WarFieldNum),
			WarRoles = lib_wasteland_war:proc_get_scene_roles(PoolId),
			case lists:keyfind(RoleId, #war_field_role.role_id, WarRoles) of
				#war_field_role{} = WarRole ->
					lib_wasteland_war:clean_hurt_list([WarRole]);
				_ ->
					ignore
			end,
			NewRoleMap = maps:put(RoleId, RoleInfo#role_info{is_in_scene = ?IN_SCENE_NO}, RoleMap),
			MaxRound = lists:max(data_wasteland_war:get_all_round()),
			case Round >= MaxRound of
				true -> add_log(Node, ZoneId, ServerId, ServerNum, RoleId, 6, Round, Rank, Score);
				_ -> add_log(Node, ZoneId, ServerId, ServerNum, RoleId, 4, Round, Rank, Score)
			end,
			update_ta_data(Node, RoleId, Round, 3, Rank),
			lib_wasteland_war:send_to_client(Node, RoleId, 60202, [?SUCCESS]);
		_ ->
			NewRoleMap = RoleMap
	end,
	NewState = State#worker_state{role_map = NewRoleMap},
	{noreply, NewState};

%% 断线重连 {'reconnect', Stage, Round, RoundEndTime, Node, RoleId}
do_handle_cast({'reconnect', Stage, Round, RoundEndTime, Node, RoleId}, State) ->
	#worker_state{zone_id = ZoneId, role_map = RoleMap, crystal_num = CrystalNumMap} = State,
	if
		Stage == ?WASTELAND_REST ->  %% 休战阶段直接退出
			lib_wasteland_war:switch_default_scene(Node, RoleId),
			NewState = State;
		true ->
			%% 战斗/准备阶段
			case maps:get(RoleId, RoleMap, []) of
				#role_info{is_over = IsOver, war_field = WarFieldNum, is_hosting = _IsHosting, is_in_scene = IsInScene,
					mon_id = MonId, ready_pool = ReadyPool} = RoleInfo
				when IsInScene == ?IN_SCENE_YES, MonId =< 0 -> %% 必选是in_scene才可以拉回/主动退出不拉回
					case IsOver of
						?IS_OVER_YES -> %% 被淘汰
							lib_wasteland_war:switch_default_scene(Node, RoleId),
							NewRoleMap = maps:put(RoleId, RoleInfo#role_info{is_in_scene = ?IN_SCENE_NO}, RoleMap);
							% lib_wasteland_war:send_to_client(Node, RoleId, 60202, [?SUCCESS]);
						_ ->
							%% 重新拉入场
							RoundScene = lib_wasteland_war:get_round_scene(Round),
							AllocLine  = case Round of
								             0 when ReadyPool =/= 0 -> ReadyPool;
								             _ -> lib_wasteland_war:get_role_pool(ZoneId, WarFieldNum)
							             end,
							{BornX, BornY} = lib_wasteland_war:get_war_pos(RoundScene),
							Args = [RoleId, RoundScene, AllocLine, 0, BornX, BornY, false, []],
							lib_wasteland_war:switch_scene_callback(Node, Args),
							{ok, BinData0} = pt_602:write(60201, [?SUCCESS]),
							%% 推送轮数
							{ok, BinData1} = pt_602:write(60206, [Round, RoundEndTime]),
							%% 推送结晶数
							CrystalNum  = maps:get(AllocLine, CrystalNumMap, 0),
							{ok, BinData2} = pt_602:write(60203, [CrystalNum]),
							%% 推送积分排行
							WarRoles = lib_wasteland_war:proc_get_scene_roles(AllocLine),
							case lists:keyfind(RoleId, #war_field_role.role_id, WarRoles) of
								#war_field_role{war_score = Score, war_rank = Rank, mon_id = MonId} ->
									case MonId =< 0 of
										true ->
											{ok, BinData3} = pt_602:write(60204, [Score, Rank]),
											BinData = <<BinData0/binary, BinData1/binary, BinData2/binary, BinData3/binary>>;
										_ ->
											BinData = <<BinData0/binary, BinData1/binary, BinData2/binary>>
									end;
								_ -> BinData = <<BinData0/binary, BinData1/binary, BinData2/binary>>
							end,
							lib_wasteland_war:send_to_client(Node, RoleId, BinData),
							NewRoleMap =  maps:put(RoleId, RoleInfo#role_info{is_in_scene = ?IN_SCENE_YES}, RoleMap)
					end;
				_ ->
					lib_wasteland_war:switch_default_scene(Node, RoleId),
					NewRoleMap = RoleMap
			end,
			NewState = State#worker_state{role_map = NewRoleMap}
	end,
	{noreply, NewState};

do_handle_cast({'host_login', Round, RoleId}, State) ->  %% todo: 必须先删除假人，再回调玩家切换场景？
	#worker_state{role_map = RoleMap, zone_id = ZoneId} = State,
	case maps:get(RoleId, RoleMap, []) of
		#role_info{mon_id = MonId, node = Node, server_id = ServerId, server_num = ServerNum, ready_pool = PoolId} = RoleInfo ->
			NewRoleInfo = RoleInfo#role_info{is_hosting = ?IN_HOSTING_NOT, mon_id = 0},
			NewRoleMap  = maps:put(RoleId, NewRoleInfo, RoleMap),
			DefaultSceneId = lib_wasteland_war:get_default_scene(),
			% PoolId = lib_wasteland_war:get_ready_pool(ZoneId),
			% 销毁假人
			mon_mgr:send({DefaultSceneId, PoolId}, MonId, {'stop', 1}),
			% 清除玩家托管信息
			unode:apply(Node, offline_hosting_data, hosting_role_delete, [RoleId]),
			%% 拉玩家进入
			{BornX, BornY} = lib_wasteland_war:get_rand_born_xy(DefaultSceneId),
			Args = [RoleId, DefaultSceneId, PoolId, 0, BornX, BornY, false, []], % {pk,{?PK_PEACE_ULTIMATE, true}}
			lib_wasteland_war:switch_scene_callback(Node, Args),
			%% 日志
			add_log(Node, ZoneId, ServerId, ServerNum, RoleId, 3, Round, 0, 0),
			NewState = State#worker_state{role_map = NewRoleMap};
		_ ->
			NewState = State
	end,
	{noreply, NewState};

do_handle_cast({'create_dummy', _ServerId, RoleId, MonId}, State) ->
	#worker_state{role_map = RoleMap, dummy_ids = DummyIds} = State,
	case maps:get(RoleId, RoleMap, []) of
		#role_info{} = RoleInfo ->
			NewRoleInfo = RoleInfo#role_info{mon_id = MonId},
			NewRoleMap  = maps:put(RoleId, NewRoleInfo, RoleMap),
			NewDummyIds = [MonId|DummyIds],
			NewState = State#worker_state{role_map = NewRoleMap, dummy_ids = NewDummyIds};
		_ ->
			NewState = State
	end,
	{noreply, NewState};

do_handle_cast({'fetch_score_rank', Node, RoleId, ServerId}, State) ->
	#worker_state{role_map = RoleMap} = State,
	RoleList = maps:values(RoleMap),
	%% 如果没有数据就去跨服找找吧
	ClsType = config:get_cls_type(),
	case RoleList of
		[] when ClsType == 0 ->
			clusters_node:apply_cast(mod_wasteland_war, fetch_score_rank, [Node, RoleId, ServerId]);
		_ ->
			WarField =
				case lists:keyfind(RoleId, #role_info.role_id, RoleList) of
					#role_info{war_field = TWarField} -> TWarField;
					_ -> 1      %% 没参加过的发送第一战场的数据
				end,
			PackFun  = fun(RoleInfo, TList) ->
				#role_info{role_id = TRoleId, rank = Rank, role_name = RoleName, power = RolePower, war_field = SWarField, sex = Sex, vip_lv = VipLv
					,sup_vip = SupVip, server_id = RServerId, server_num = ServerNum, round = Round, kill_num = KillNum,
					max_combo = MaxCombo, score = Score} = RoleInfo,
				case Rank =< ?RANK_LENGTH of
					true when SWarField == WarField->
						[{Rank, TRoleId, RoleName, RolePower, Sex, VipLv, SupVip, RServerId, ServerNum, Round, KillNum, MaxCombo, Score}|TList];
					_ ->
						TList
				end end,
			PackList = lists:foldl(PackFun, [], RoleList),
			case lists:keyfind(RoleId, #role_info.role_id, RoleList) of
				#role_info{rank = MyRank, round = MyRound, kill_num = MyKillNum, max_combo = MyCombo, score = MyScore} ->
					ignore;
				_ -> MyRank = 0, MyRound = 0, MyKillNum = 0, MyCombo = 0, MyScore = 0
			end,
			lib_wasteland_war:send_to_client(Node, RoleId, 60205, [PackList, MyRank, MyRound, MyKillNum, MyCombo, MyScore])
	end,
	{noreply, State};

do_handle_cast({'notice_round', Stage, Round, RoundEndTime, Node, RoleId}, State) ->
	#worker_state{zone_id = ZoneId, role_map = RoleMap, crystal_num = CrystalNumMap} = State,
	case Stage =/= ?WASTELAND_REST of
		true ->
			case maps:get(RoleId, RoleMap, []) of
				#role_info{war_field = WarFieldNum} ->
					PoolId = lib_wasteland_war:get_role_pool(ZoneId, WarFieldNum),
					CrystalNum = maps:get(PoolId, CrystalNumMap, 0);
				_ ->
					CrystalNum = 0
			end,
			{ok, BinData0} = pt_602:write(60203, [CrystalNum]),
			{ok, BinData1} = pt_602:write(60206, [Round, RoundEndTime]),
			%% 预备期切换场景后不推送60212协议
			{ok, BinData2} = pt_602:write(60212, [?SUCCESS]),
			case State of
				?WASTELAND_READY -> BinData = <<BinData0/binary, BinData1/binary>>;
				_ -> BinData = <<BinData0/binary, BinData1/binary, BinData2/binary>>
			end,
			lib_wasteland_war:send_to_client(Node, RoleId, BinData);
		_ ->
			ignore
	end,
	{noreply, State};

%% 注意:准备阶段战力变更,将影响分战区计算。
do_handle_cast({'power_change', RoleId, ServerId, Power}, State) ->
	#worker_state{role_map = RoleMap} = State,
	case maps:get(RoleId, RoleMap, []) of
		#role_info{} = RoleInfo ->
			NewRoleInfo = RoleInfo#role_info{power = Power},
			NewRoleMap  = maps:put(RoleId, NewRoleInfo, RoleMap),
			lib_wasteland_war:db_update_rank_power(Power, ServerId, RoleId),
			NewState    = State#worker_state{role_map = NewRoleMap};
		_ ->
			NewState = State
	end,
	{noreply, NewState};

%% 玩家形象改变,排行榜数据相应改变  figure_change
do_handle_cast({'figure_change', RoleId, ServerId, Type, ChangeValue}, State) ->
	#worker_state{role_map = RoleMap} = State,
	case maps:get(RoleId, RoleMap, []) of
		#role_info{} = RoleInfo ->
			NewRoleInfo = case Type of
				              ?CHANGE_TYPE_VIP ->
					              lib_wasteland_war:db_update_rank_vip(ChangeValue, ServerId, RoleId),
					              RoleInfo#role_info{vip_lv = ChangeValue};
				              ?CHANGE_TYPE_SUP_VIP ->
					              lib_wasteland_war:db_update_rank_sup_vip(ChangeValue, ServerId, RoleId),
					              RoleInfo#role_info{sup_vip = ChangeValue};
				              ?CHANGE_TYPE_NAME ->
					              lib_wasteland_war:db_update_rank_name(ChangeValue, ServerId, RoleId),
					              RoleInfo#role_info{role_name = ChangeValue};
				              _ -> RoleInfo
			              end,
			NewRoleMap = maps:put(RoleId, NewRoleInfo, RoleMap),
			NewState   = State#worker_state{role_map = NewRoleMap};
		_ ->
			NewState = State
	end,
	{noreply, NewState};

do_handle_cast({'kill_player', Round, KillId, BeKillId, HurtList}, State) ->
	#worker_state{zone_id = ZoneId, role_map = RoleMap} = State,
	Killer   = maps:get(KillId, RoleMap, []),
	BeKiller = maps:get(BeKillId, RoleMap, []),
	%% 需要判断是否已经被淘汰,因为有延迟结算时间
	if
		Killer#role_info.is_over == ?IS_OVER_YES ->
			NewState = State;
		true ->
			%% 计算可获得积分 和 记录连杀
			{KillerGetScore, RoleMap1} = lib_wasteland_war:calc_killer_obtain_score(RoleMap, Killer, BeKiller),
			NewKiller = maps:get(KillId, RoleMap1, []),
			{IsCombo, Combo} = lib_wasteland_war:check_kill_combo(NewKiller),
			%% 判断被击杀者是否被终结连杀
			{IsStopCombo, NewBeKiller} = lib_wasteland_war:check_stop_combo(BeKiller),
			SceneId = lib_wasteland_war:get_round_scene(Round),
			PoolId  = lib_wasteland_war:get_role_pool(ZoneId, Killer#role_info.war_field),
			IsCombo andalso lib_wasteland_war:notice_kill_combo(NewKiller#role_info.node, NewKiller#role_info.role_id, Combo, NewKiller#role_info.mon_id),
			IsStopCombo andalso lib_wasteland_war:notice_stop_kill(PoolId, NewKiller, NewBeKiller),
			%% 传闻
			IsCombo andalso lib_wasteland_war:notice_combo_tv(NewKiller#role_info.role_name, KillId, Combo, ZoneId, SceneId, PoolId),
			IsStopCombo andalso lib_wasteland_war:notice_end_combo_tv(NewKiller#role_info.role_name, KillId,
				NewBeKiller#role_info.role_name, BeKillId, BeKiller#role_info.combo, ZoneId, SceneId, PoolId),
			RoleMap2 = case NewBeKiller of
				           #role_info{} ->
					           maps:put(BeKillId, NewBeKiller, RoleMap1);
				           _ ->
					           RoleMap1
			           end,
			%% 伤害者加助攻积分,此处先不重新计算排行,由下面的recalc执行
			RoleMap3 = lib_wasteland_war:add_assist_score(PoolId, KillId, BeKillId, HurtList, RoleMap2),
			%% 分区重新排名
			SortRoleMap = lib_wasteland_war:recalc_role_rank(KillId, KillerGetScore, RoleMap3, ZoneId, Round),
			%% 记录TA数据
			lib_wasteland_war:pro_add_kill_role_score(KillId, KillerGetScore),
			NewState = State#worker_state{role_map = SortRoleMap}
	end,
	{noreply, NewState};

do_handle_cast({'kill_crystal', Round, KillId}, State) ->
	#worker_state{zone_id = ZoneId, crystal_num = CrystalNumMap, role_map = RoleMap} = State,
	BaseScore = data_wasteland_war:get_kv_cfg(crystal_kill_grade),
	{NewRoleMap, NewCrystalNumMap} =
		case maps:get(KillId, RoleMap, []) of
			#role_info{war_field = WarFieldNum} ->
				PoolId = lib_wasteland_war:get_role_pool(ZoneId, WarFieldNum),
				CrystalNum = maps:get(PoolId, CrystalNumMap, 0),
				NewCrystalNum = max(CrystalNum-1, 0),
				SortRoleMap = lib_wasteland_war:recalc_role_rank(KillId, BaseScore, RoleMap, ZoneId, Round),
				%% 推送结晶数量
				SceneId = lib_wasteland_war:get_round_scene(Round),
				{ok, BinData} = pt_602:write(60203, [NewCrystalNum]),
				lib_server_send:send_to_scene(SceneId, PoolId, BinData),
				%% 记录TA数据
				lib_wasteland_war:pro_add_kill_crystal_score(KillId, BaseScore),
				{SortRoleMap, maps:put(PoolId, NewCrystalNum, CrystalNumMap)};
			_ ->
				{RoleMap, CrystalNumMap}
		end,
	NewState = State#worker_state{role_map = NewRoleMap, crystal_num = NewCrystalNumMap},
	{noreply, NewState};

do_handle_cast({'record_die_state', RoleId}, State) ->
	#worker_state{role_map = RoleMap} = State,
	NewRoleMap = case maps:get(RoleId, RoleMap, []) of
		             #role_info{} = RoleInfo ->
			             NewRoleInfo = RoleInfo#role_info{is_die = ?IN_DIE_YES},
			             maps:put(RoleId, NewRoleInfo, RoleMap);
		             _ ->
			             RoleMap
	             end,
	NewState = State#worker_state{role_map = NewRoleMap},
	{noreply, NewState};

do_handle_cast({'clean_die_state', RoleId}, State) ->
	#worker_state{role_map = RoleMap} = State,
	NewRoleMap = case maps:get(RoleId, RoleMap, []) of
		              #role_info{} = RoleInfo ->
			              NewRoleInfo = RoleInfo#role_info{is_die = ?IN_DIE_NOT},
			              maps:put(RoleId, NewRoleInfo, RoleMap);
		              _ ->
			              RoleMap
	              end,
	%% 记录TA数据
	lib_wasteland_war:pro_record_revive_times(RoleId),
	NewState = State#worker_state{role_map = NewRoleMap},
	{noreply, NewState};

%% ---------------------------------------------------------------------------
%% 'calc_war_field'
do_handle_cast({'calc_war_field'}, State) ->
	#worker_state{zone_id = ZoneId, role_map = RoleMap} = State,
	case data_wasteland_war:get_round_cfg(1) of
		#wasteland_round_reward_cfg{max_role = MaxRole} -> skip;
		_ -> MaxRole = ?DEFAULT_ROLE_NUM
	end,
	RoleList = maps:values(RoleMap),
	RoleList1 = [RoleInfo||#role_info{is_in_scene = IsInScene} = RoleInfo<-RoleList, IsInScene == ?IN_SCENE_YES],
	RoleNum = length(RoleList1),
	%% 分战场数量
	MaxWarFieldNum = umath:ceil(RoleNum/MaxRole),
	SortRoles     = sort_with_combat_power(RoleList1),
	SceneRolesMap = trans_to_scene_role(SortRoles, 1, #{}),
	SceneRoles = maps:values(SceneRolesMap),
	%% 玩家选择战场
	case MaxWarFieldNum == 1 of
		true -> WarFieldMap = select_one_war_field(SceneRoles, #{});
		_    -> WarFieldMap = select_war_field(SceneRoles, MaxWarFieldNum, #{})
	end,
	Pools = lib_wasteland_war:get_all_pools(ZoneId, WarFieldMap),
	lib_wasteland_war:proc_put_all_scene_roles(ZoneId, WarFieldMap),
	%% 拉人进场景
	NewRoleMap = pull_role_into_war(RoleList1, ZoneId, WarFieldMap),

	NewState = State#worker_state{pool_list = Pools, role_map = NewRoleMap, round = 1},
	{noreply, NewState};

do_handle_cast({'create_crystal', Round}, State) ->
	#worker_state{pool_list = Pools, crystal_num = CrystalNumMap} = State,
	NewCrystalNum = lib_wasteland_war:create_crystal(Pools, Round, CrystalNumMap),
	NewState   = State#worker_state{crystal_num = NewCrystalNum},
	{noreply, NewState};

do_handle_cast({'next_round', OldRound}, State) ->
	#worker_state{zone_id = ZoneId, pool_list = Pools, role_map = RoleMap
		,delay_reward_ref = DelayRewardRef, force_out_ref = ForceOutRef
	} = State,
	%% 进行排行计算，淘汰玩家
	MaxRound = lists:max(data_wasteland_war:get_all_round()),
	case OldRound == MaxRound of
		%% 最后一轮,进行终结
		true ->
			%% 此处保留托管玩家的host_reward，最后加入host_log里面
			RoleMap1 = lib_wasteland_war:settle_reward_and_finish_war(ZoneId, Pools, OldRound, RoleMap),
			%% 存储排行数据
			spawn(fun() -> lib_wasteland_war:save_role_list(RoleMap1) end),
			%% 全员马上退出场景结算
			NewDelayRewardRef = util:send_after(DelayRewardRef, 10, self(), {'delay_send_reward'}),
			NewForceOutRef    = util:send_after(ForceOutRef, 500, self(), {'force_exit'}),
			% NewRoleList = lib_wasteland_war:settle_reward_and_finish_war(Pools, OldRound, RoleList),
			%% 清理场景怪物(ps:最后一个场景的场景进程仍保留,因有延后的消息处理)
			case config:get_cls_type() of
				1 -> AllScene = data_wasteland_war:get_cls_all_scene() -- [?CLS_WAR_LAST_SCENE];
				_ -> AllScene = data_wasteland_war:get_all_scene() -- [?LOCAL_WAR_LAST_SCENE]
			end,
			CleanFun = fun(SceneId) ->
				lists:foreach(fun(TPoolId) ->
					mod_scene_agent:clear_scene(SceneId, TPoolId, 0),
					lib_mon:clear_scene_dummy(SceneId, TPoolId, 0, 1)
				              end, Pools)
			           end,
			lists:foreach(CleanFun, AllScene),
			%% 准备场景清理
			spawn(fun() ->
				DefaultScene  = lib_wasteland_war:get_default_scene(),
				PoolFun = fun(_, RoleInfo, TList) ->
					case RoleInfo of
						#role_info{ready_pool = TRPool} -> [TRPool|lists:delete(TRPool, TList)];
						_ -> TList
					end end,
				DefaultPoolList = maps:fold(PoolFun, [], RoleMap),
				ForFun = fun(TPoolId) ->
					mod_scene_agent:clear_scene(DefaultScene, TPoolId, 0),
					lib_mon:clear_scene_dummy(DefaultScene, TPoolId, 0, 1)
				end,
				lists:map(ForFun, DefaultPoolList)
			end),
			NewState = State#worker_state{role_map = RoleMap1, crystal_num = #{},
				delay_reward_ref = NewDelayRewardRef, force_out_ref = NewForceOutRef};
		_ ->
			%% 发放奖励 并 划分进入下一轮玩家
			RoleMap1 = lib_wasteland_war:settle_reward_and_take_next_round(Pools, OldRound, RoleMap),
			%% 淘汰者延时发放奖励 & 强制退出场景
			NewDelayRewardRef = util:send_after(DelayRewardRef, 5*100, self(), {'delay_send_reward'}),
			NewForceOutRef    = util:send_after(ForceOutRef, 15*100, self(), {'force_exit'}),
			%% 清理场景结晶
			% SceneId = lib_wasteland_war:get_round_scene(OldRound),
			% lists:foreach(fun(TPoolId) ->
			%	mod_scene_agent:clear_scene(SceneId, TPoolId, 0)
				% lib_mon:clear_scene_dummy(SceneId, TPoolId, 0, 1)
			%              end, Pools),
			NewState = State#worker_state{role_map = RoleMap1, crystal_num = #{}, delay_reward_ref = NewDelayRewardRef,
				force_out_ref = NewForceOutRef
			}
	end,
	{noreply, NewState};

do_handle_cast({'take_next_round', Round}, State) ->
	#worker_state{pool_list = PoolList, role_map = RoleMap} = State,
	%% 拉玩家进入下一场
	SceneId    = lib_wasteland_war:get_round_scene(Round),
	WarPosList = lib_wasteland_war:get_war_pos_list(SceneId),
	NextFun = fun(PoolId) ->
		%% 提前创建场景 避免并行的信息发送创建场景导致获取mon_mgr进程pid失败的情况
		mod_scene_agent:get_scene_pid(SceneId, PoolId),
		WarRoles  = lib_wasteland_war:proc_get_scene_roles(PoolId),
		RoleIds   = [{RoleId, Node, ServerId, MonId}||
			#war_field_role{role_id = RoleId, node = Node, server_id = ServerId, mon_id = MonId}<-WarRoles],
		F = fun({TRoleId, TNode, _TServerId, TMonId}, TWarPosList) ->
			IsInScene = lib_wasteland_war:is_in_scene(TRoleId, RoleMap),
			% {BornX, BornY} = lib_wasteland_war:get_rand_born_xy(SceneId),
			{BornX, BornY, TWarPosList1} = case TWarPosList of
				               [{TBornX, TBornY}|T] -> {TBornX, TBornY, T};
				               _ ->
					               case WarPosList of
						               [{TBornX, TBornY}|T] -> {TBornX, TBornY, T};
						               _ -> {4977, 3263, []}
					               end
			               end,
			%% 晋级时玩家死亡则直接拉活
			IsDie = lib_wasteland_war:is_die(TRoleId, RoleMap),
			HpLim = lib_wasteland_war:get_hp_lim(TRoleId, RoleMap),
			case IsDie of
				true -> ExtraArgs = [{change_scene_hp_lim, max(20000, HpLim)}];
				_    -> ExtraArgs = []
			end,
			Args = [TRoleId, SceneId, PoolId, 0, BornX, BornY, false, [{pk,{?PK_ALL, true}}] ++ ExtraArgs],
			%% 切换场景后在推送最新轮数
			% Round =/= 1 andalso lib_wasteland_war:db_update_rank_round(Round, TServerId, TRoleId),
			% IsInScene andalso lib_wasteland_war:send_to_client(TNode, TRoleId, 60206, [Round, RoundEndTime]),
			(IsInScene andalso TMonId =< 0) andalso lib_wasteland_war:switch_scene_callback(TNode, Args),
			(IsInScene andalso TMonId > 0) andalso
			begin
				case IsDie of true -> MonArgs = [{hp, max(20000, HpLim)}]; _ -> MonArgs = [] end,
				unode:apply(TNode, offline_hosting_dummy, host_behavior_change_scene, [TRoleId, SceneId, PoolId, 0, BornX, BornY, MonArgs])
			end,
			TWarPosList1
		end,
		lists:foldl(F, WarPosList, RoleIds)
	end,
	lists:foreach(NextFun, PoolList),
	%% 发放当前一轮轮数奖励
	SendAwardFun = fun(PoolId, TRoleMap) ->
		WarRoles = lib_wasteland_war:proc_get_scene_roles(PoolId),
		%% 清空伤害列表
		lib_wasteland_war:clean_hurt_list(WarRoles),
		TRoleMap1 = lib_wasteland_war:send_round_reward(Round, WarRoles, TRoleMap),
		TRoleMap1
	end,
	NewRoleMap = lists:foldl(SendAwardFun, RoleMap, PoolList),
	{noreply, State#worker_state{role_map = NewRoleMap, round = Round}};

%% 上报玩家战场积分排行
do_handle_cast({'sync_role_score_rank'}, State) ->
	#worker_state{zone_id = ZoneId, role_map = RoleMap} = State,
	F = fun(_, RoleInfo, AccList) ->
		case RoleInfo of
			#role_info{role_id = RoleId, war_field = WarField, score = Score, rank = Rank} ->
				[{RoleId, WarField, Score, Rank}|AccList];
			_ ->
				AccList
		end end,
	SyncList = maps:fold(F, [], RoleMap),
	mod_wasteland_war:push_role_score_rank(ZoneId, SyncList),
	{noreply, State};

do_handle_cast({'close_worker'}, State) ->
	#worker_state{zone_id = ZoneId, role_map = RoleMap,
		pool_list = Pools
	} = State,
	%% 清理所有玩家
	lib_wasteland_war:clean_scene_player(RoleMap, [ZoneId]),
	%% 清理场景怪物数据
	case config:get_cls_type() of
		1 -> AllScene = data_wasteland_war:get_cls_all_scene();
		_ -> AllScene = data_wasteland_war:get_all_scene()
	end,
	CleanFun = fun(SceneId) ->
		lists:foreach(fun(TPoolId) ->
			mod_scene_agent:clear_scene(SceneId, TPoolId, 0),
			lib_mon:clear_scene_dummy(SceneId, TPoolId, 0, 1)
		              end, Pools)
	end,
	lists:foreach(CleanFun, AllScene),
	%% 清理进程字典数据(战场玩家数据&推送数据间隔&TA数据)
	erase(),
	{stop, normal, #worker_state{}};

%% 与正常关闭不同的是会多一个发放奖励操作
do_handle_cast({'gm_end', Round}, State) ->
	#worker_state{zone_id = ZoneId, role_map = RoleMap,
		pool_list = Pools
	} = State,
	lib_wasteland_war:settle_reward_and_finish_war(ZoneId, Pools, Round, RoleMap),
	F = fun(_, #role_info{role_id = RoleId} = RoleInfo, TRoleMap) ->
		maps:put(RoleId, RoleInfo#role_info{is_over = 1, is_in_scene = 0}, TRoleMap)
	end,
	NewRoleMap = maps:fold(F, RoleMap, RoleMap),
	%% 存储排行数据
	spawn(fun() -> lib_wasteland_war:save_role_list(NewRoleMap) end),
	%% 推送活动结束
	%lib_wasteland_war:notice_act_end(ZoneId, 0, "gm", 1),
	%% 清理所有玩家
	lib_wasteland_war:clean_scene_player(RoleMap, [ZoneId]),
	%% 清理场景怪物数据
	case config:get_cls_type() of
		1 -> AllScene = data_wasteland_war:get_cls_all_scene();
		_ -> AllScene = data_wasteland_war:get_all_scene()
	end,
	CleanFun = fun(SceneId) ->
		lists:foreach(fun(TPoolId) ->
			mod_scene_agent:clear_scene(SceneId, TPoolId, 0),
			lib_mon:clear_scene_dummy(SceneId, TPoolId, 0, 1)
		              end, Pools)
	           end,
	lists:foreach(CleanFun, AllScene),
	%% 准备场景清理
	spawn(fun() ->
		DefaultScene = lib_wasteland_war:get_default_scene(),
		PoolFun = fun(_, RoleInfo, TList) ->
			case RoleInfo of
				#role_info{ready_pool = TRPool} -> [TRPool|lists:delete(TRPool, TList)];
				_ -> TList
			end end,
		DefaultPoolList = maps:fold(PoolFun, [], RoleMap),
		ForFun = fun(TPoolId) ->
			mod_scene_agent:clear_scene(DefaultScene, TPoolId, 0),
			lib_mon:clear_scene_dummy(DefaultScene, TPoolId, 0, 1)
		end,
		lists:map(ForFun, DefaultPoolList)
	end),
	%% 清理进程字典数据(战场玩家数据)
	erase(),
	% {stop, normal, #worker_state{}};
	{noreply, State#worker_state{role_map = NewRoleMap}};

do_handle_cast(_Msg, State) ->
	?ERR("Handle unknow msg[~w]~n", [_Msg]),
	{noreply, State}.

%% ---------------------------------------------------------------------------
%% 发放奖励
do_handle_info({'delay_send_reward'}, State) ->
	#worker_state{pool_list = PoolList, role_map = RoleMap,
		delay_reward_ref = DelayRewardRef
	} = State,
	util:cancel_timer(DelayRewardRef),
	SendAwardFun = fun(PoolId, TRoleMap) ->
		WarRoles = lib_wasteland_war:proc_get_exit_roles(PoolId),
		case WarRoles of
			[_WarRole|_] ->
				lib_wasteland_war:send_rank_reward(WarRoles, TRoleMap);
			_ ->
				TRoleMap
		end
	end,
	NewRoleMap = lists:foldl(SendAwardFun, RoleMap, PoolList),
	{noreply, State#worker_state{role_map = NewRoleMap, delay_reward_ref = []}};

%% 强制退出
do_handle_info({'force_exit'}, State) ->
	#worker_state{zone_id = ZoneId, pool_list = PoolList, role_map = RoleMap,
		force_out_ref = ForceOutRef, round = Round, dummy_ids = DummyIds} = State,
	util:cancel_timer(ForceOutRef),
	MaxRound = lists:max(data_wasteland_war:get_all_round()),
	ExitFun = fun(PoolId, TRoleMap) ->
		WarRoles = lib_wasteland_war:proc_get_exit_roles(PoolId),
		case WarRoles of
			[_WarRole|_] ->
				F = fun(#war_field_role{role_id = TRoleId, mon_id = TMonId}, TRoleMap1) ->
					case maps:get(TRoleId, TRoleMap1, []) of
						#role_info{node = Node, is_in_scene = IsInScene, server_id = ServerId, server_num = ServerNum
							,score = Score, rank = Rank} = RoleInfo ->
							Args = [TRoleId, 0, 0, 0, []],
							case TMonId > 0 of
								true ->
									unode:apply(Node, offline_hosting_wasteland_war, cast_dummy_out, [TRoleId]);
								_ ->
									IsInScene == 1 andalso lib_wasteland_war:switch_scene_callback(Node, Args)
							end,
							case Round == MaxRound of
								true ->
									add_log(Node, ZoneId, ServerId, ServerNum, TRoleId, 6, Round, Rank, Score),
									update_ta_data(Node, TRoleId, Round, 1, Rank);
								_ ->
									add_log(Node, ZoneId, ServerId, ServerNum, TRoleId, 5, Round, Rank, Score),
									update_ta_data(Node, TRoleId, Round, 2, Rank)
							end,
							maps:put(TRoleId, RoleInfo#role_info{is_in_scene = ?IN_SCENE_NO}, TRoleMap1);
						_ ->
							TRoleMap1
					end end,
				lists:foldl(F, TRoleMap, WarRoles);
			_ ->
				TRoleMap
		end end,
	NewRoleMap	= lists:foldl(ExitFun, RoleMap, PoolList),
	%% 最后一轮清理数据,此处pool_list不能清理
	case Round == MaxRound of
		true -> NewDummyIds = [];
		_ -> NewDummyIds = DummyIds
	end,
	NewState = State#worker_state{role_map = NewRoleMap, dummy_ids = NewDummyIds, force_out_ref = []},
	{noreply, NewState};

do_handle_info(_Info, State) ->
	?ERR("Handle unknow msg[~w]~n", [_Info]),
	{noreply, State}.

%% ---------------------------  builtin function -------------------------------------
%% 根据战力排行
sort_with_combat_power(RoleList) ->
	SortFun = fun(#role_info{power = PowerA}, #role_info{power = PowerB}) ->
		case PowerA >= PowerB of
			true -> true;
			_ -> false
		end end,
	lists:sort(SortFun, RoleList).

%% 转换成战场角色,只用于计算战力划分战场(用map存储中间数据比较好)
trans_to_scene_role([], _PowerRank, SceneRoles) -> SceneRoles;
trans_to_scene_role([SortRole|List], PowerRank, SceneRoles) ->
	#role_info{role_id = RoleId, server_id = ServerId, node = Node, mon_id = MonId} = SortRole,
	SceneRole = #war_field_role{role_id = RoleId, mon_id = MonId, server_id = ServerId, power_rank = PowerRank, node = Node},
	NewSceneRoles = maps:put(RoleId, SceneRole, SceneRoles),
	trans_to_scene_role(List, PowerRank+1, NewSceneRoles).

%% 选择战区
select_war_field([], _MaxWarFieldNum, WarFieldMap) -> WarFieldMap;
select_war_field([SceneRole|List], MaxWarFieldNum, WarFieldMap) ->
	#war_field_role{role_id = RoleId, power_rank = Rank} = SceneRole,
	WarFiledNum = Rank rem MaxWarFieldNum,
	%% 如果余数为0,则分战场为最大分战场数
	WarFiledNum1 =
		case WarFiledNum == 0 of
			true -> MaxWarFieldNum;
			_    -> WarFiledNum
		end,
	NewWarFieldMap =
		case maps:get(WarFiledNum1, WarFieldMap, []) of
			[] ->
				maps:put(WarFiledNum1, [SceneRole], WarFieldMap);
			RoleList ->
				NewRoleList = lists:keystore(RoleId, #war_field_role.role_id, RoleList, SceneRole),
				maps:put(WarFiledNum1, NewRoleList, WarFieldMap)
		end,
	select_war_field(List, MaxWarFieldNum, NewWarFieldMap).

%% 人数只满足开一个分战场
select_one_war_field([], WarFieldMap) -> WarFieldMap;
select_one_war_field([SceneRole|List], WarFieldMap) ->
	#war_field_role{role_id = RoleId} = SceneRole,
	NewWarFieldMap =
		case maps:get(1, WarFieldMap, []) of
			[] ->
				maps:put(1, [SceneRole], WarFieldMap);
			RoleList ->
				NewRoleList = lists:keystore(RoleId, #war_field_role.role_id, RoleList, SceneRole),
				maps:put(1, NewRoleList, WarFieldMap)
		end,
	select_one_war_field(List, NewWarFieldMap).

%% 拉人进场
pull_role_into_war(RoleList, ZoneId, WarFieldMap) ->
	WarFieldNums = maps:keys(WarFieldMap),
	%% 重新构造map数据
	F0 = fun(#role_info{role_id = RoleId} = RoleInfo, TRoleMap0) ->
			maps:put(RoleId, RoleInfo, TRoleMap0)
	     end,
	RoleMap = lists:foldl(F0, #{}, RoleList),
	%% 提前创建场景 避免并行的信息发送创建场景导致获取mon_mgr进程pid失败的情况
	F = fun(WarFieldNum, TRoleMap) ->
		SceneId = lib_wasteland_war:get_round_scene(1),
		PoolId  = lib_wasteland_war:get_role_pool(ZoneId, WarFieldNum),
		mod_scene_agent:get_scene_pid(SceneId, PoolId),
		SceneRoles = maps:get(WarFieldNum, WarFieldMap, []),
		WarPosList = lib_wasteland_war:get_war_pos_list(SceneId),
		RoleMap1 = do_pull_role(WarFieldNum, TRoleMap, SceneId, PoolId, SceneRoles, WarPosList, WarPosList, ZoneId),
		RoleMap1
	    end,
	NewRoleMap = lists:foldl(F, RoleMap, WarFieldNums),
	NewRoleMap.

do_pull_role(_WarFieldNum, RoleMap, _SceneId, _PoolId, [], _WarPosList, _AllWarPosList, _ZoneId) -> RoleMap;
do_pull_role(WarFieldNum, RoleMap, SceneId, PoolId, [SceneRole|Left], WarPosList, AllWarPosList, ZoneId) ->
	case SceneRole of
		#war_field_role{role_id = RoleId, node = Node} -> ok;
		_ -> RoleId = 0, Node = undefined
	end,
	%% 推送最新轮数
	case maps:get(RoleId, RoleMap, []) of
		#role_info{is_in_scene = IsInScene, is_hosting = IsHosting, hp_lim = HpLim, mon_id = _MonId, ready_pool = _ReadyPool} = RoleInfo ->
			case IsHosting == ?IN_HOSTING of
				true ->
					%% 托管这需要先存储第一轮轮数奖励
					RoundOneReward = get_round_one_reward(),
					NewRoleMap = maps:put(RoleId, RoleInfo#role_info{war_field = WarFieldNum, round = 1, host_reward = RoundOneReward}, RoleMap);
				_->
					NewRoleMap = maps:put(RoleId, RoleInfo#role_info{war_field = WarFieldNum, round = 1}, RoleMap)
			end;
		_ -> IsInScene = 0, IsHosting = 0, HpLim = 0,
			NewRoleMap = RoleMap
	end,
	% {BornX, BornY} = lib_wasteland_war:get_rand_born_xy(SceneId),
	{BornX, BornY, WarPosList1} = case WarPosList of
		                               [{TBornX, TBornY}|T] -> {TBornX, TBornY, T};
		                               _ -> %% 当坐标点已经循环完一轮之后
			                               case AllWarPosList of
				                               [{TBornX, TBornY}|T] -> {TBornX, TBornY, T};
				                               _ -> {4977, 3263, []}
			                               end
	                               end,
	Args = [RoleId, SceneId, PoolId, 0, BornX, BornY, false, [{pk,{?PK_ALL, true}}, {change_scene_hp_lim, max(20000, HpLim)}]],
	%% 真实玩家处理
	(IsInScene == ?IN_SCENE_YES andalso IsHosting == ?IN_HOSTING_NOT) andalso
		unode:apply(Node, ?MODULE, do_pull_role_on_node, [RoleId, Args]),
	%% 托管玩家切换场景 && 触发战斗
	% DefaultSceneId = lib_wasteland_war:get_default_scene(),
	% DefaultPoolId  = lib_wasteland_war:get_ready_pool(ZoneId),
	IsHosting == ?IN_HOSTING andalso
	begin
		%% 协议必须按顺序
		unode:apply(Node, offline_hosting_dummy, host_behavior_change_scene, [RoleId, SceneId, PoolId, 0, BornX, BornY, []])
		% mon_mgr:send({DefaultSceneId, ReadyPool}, MonId, {change_scene, SceneId, PoolId, 0, BornX, BornY, []}),
		% mon_mgr:send({DefaultSceneId, ReadyPool}, MonId, {offline_hosting_behavior, 'ATTACK', []})
	end,
	do_pull_role(WarFieldNum, NewRoleMap, SceneId, PoolId, Left, WarPosList1, AllWarPosList, ZoneId).

do_pull_role_on_node(RoleId, ChangeSceneArgs) ->
	%% 推送第一轮的结晶数
	case data_wasteland_war:get_round_cfg(1) of
		#wasteland_round_reward_cfg{max_crystal = MaxCrystal} -> skip;
		_ -> MaxCrystal = 10
	end,
	{ok, BinData} = pt_602:write(60203, [MaxCrystal]),
	lib_server_send:send_to_uid(RoleId, BinData),
	%% 在场且不是托管的才发放第一轮轮次奖励
	send_round_reward_to_role(RoleId),
	erlang:apply(lib_scene, change_player_scene, ChangeSceneArgs),
	ok.

%% 发放第一轮的轮数奖励
send_round_reward_to_role(RoleId) ->
	case data_wasteland_war:get_round_cfg(1) of
		#wasteland_round_reward_cfg{reward = Reward} ->
			Title  = utext:get(6020007), Content = utext:get(6020008),
			Produce = #produce{type = wasteland_war, reward = Reward, title = Title, content = Content},
			lib_goods_api:send_reward_with_mail(RoleId, Produce);
		_ ->
			ignore
	end.

%% 获取第一轮次的奖励
get_round_one_reward() ->
	case data_wasteland_war:get_round_cfg(1) of
		#wasteland_round_reward_cfg{reward = Reward} ->
			Reward;
		_ -> []
	end.

%% 进入场景后的事件触发
after_enter_scene(Node, RoleId, IsHosting, LogMsg) ->
	%% 记录TA数据
	lib_wasteland_war:pro_record_enter_time(RoleId),
	[ZoneId, ServerId, ServerNum, Round] = LogMsg,
	case IsHosting of
		0 -> CastLogMsg = [ZoneId, ServerId, ServerNum, RoleId, 1, Round, 0, 0];
		_ -> CastLogMsg = [ZoneId, ServerId, ServerNum, RoleId, 2, Round, 0, 0]
	end,
	unode:apply(Node, ?MODULE, after_enter_scene_on_node, [RoleId, CastLogMsg]),
	ok.

after_enter_scene_on_node(RoleId, CastLogMsg) ->
	%% 嗨点,第一次进入触发
	mod_hi_point:success_end(RoleId, ?MOD_WASTELAND_WAR, 0),
	lib_player:apply_cast(RoleId, ?APPLY_CAST,
		custom_act_adventure, do_trigger_adventure_task, [RoleId, ?MOD_WASTELAND_WAR, 0, 1]),
	lib_achievement_api:async_event(RoleId, lib_achievement_api, wasteland_war_event, 1),
	[ZoneId, ServerId, ServerNum, RoleId, Type, Round, Rank, Score] = CastLogMsg,
	node_add_log(ZoneId, ServerId, ServerNum, RoleId, Type, Round, Rank, Score),
	ok.

%% 日志
add_log(Node, ZoneId, ServerId, ServerNum, RoleId, Type, Round, Rank, Score) ->
	unode:apply(Node, ?MODULE, node_add_log, [ZoneId, ServerId, ServerNum, RoleId, Type, Round, Rank, Score]).

node_add_log(ZoneId, ServerId, ServerNum, RoleId, Type, Round, Rank, Score) ->
	KfGrade = case czone_api:env_get_zone() of
		          #czone_svr_local{stage = Stage} -> Stage;
		          _ -> 1
	          end,
	lib_log_api:log_wasteland_war(ZoneId, ServerId, ServerNum, KfGrade, RoleId, Type, Round, Rank, Score).

%% TA数据记录
update_ta_data(Node, RoleId, Round, ResultType, Rank) ->
	{ReviveTimes, KillScore, CrystalScore, StartTime} = lib_wasteland_war:pro_get_role_ta_data(RoleId),
	UseTime = max(0, utime:unixtime() - StartTime),
	unode:apply(Node, ?MODULE, node_update_ta_data, [RoleId, Round, ResultType, CrystalScore, KillScore, Rank, ReviveTimes, UseTime]).

node_update_ta_data(RoleId, Round, ResultType, CrystalScore, KillScore, Rank, ReviveTimes, UseTime) ->
	lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?HAND_OFFLINE,
		?MODULE, update_ta_data_on_player, [[Round, ResultType, CrystalScore, KillScore, Rank, ReviveTimes, UseTime]]),
	ok.

update_ta_data_on_player(Player, [Round, ResultType, CrystalScore, KillScore, Rank, ReviveTimes, UseTime]) ->
	ta_agent_fire:wasteland_war_kv(Player, [Round, ResultType, CrystalScore, KillScore, Rank, ReviveTimes, UseTime]),
	{ok, Player}.