%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%
%%% 1、开始，拉玩家上线参与玩法，因为流程较长，所以spawn一个进程调用 take_all_part_in
%%% 2、结束，需要踢玩家下线，调用 notice_logout，需要停止流程1里面的进程，
%%%    因为拉玩家上线是排队上线的，所以有可能活动结束的时候，该进程还没有结束，所以需要强制结束，防止将玩家拉上线后无法参与玩法，又无法下线。
%%% 3、玩家的奖励由于有多种获得方式，非通过lib_goods_api:send_reward发放的奖励，必须调用行为 host_behavior_reward 记录奖励
%%% 4、托管开始1分钟后才会扣除托管值，所以1分钟内玩家上线是不会消耗托管值的
%%% 5、托管日志会在托管结束的时候才进行保存
%%% @end
%%%-------------------------------------------------------------------
-module(offline_hosting).
-author("liushl").

-include("server.hrl").
-include("common.hrl").
-include("predefine.hrl").
-include("attr.hrl").
-include("offline_hosting.hrl").
-include("role.hrl").
-include("login.hrl").
-include("def_module.hrl").
-include("figure.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-include("server_kv.hrl").
-behaviour(gen_server).
%% API
-export([
    take_all_part_in/4
    , take_part_in/4             %% 离线托管：将离线玩家拉上线参与活动
    , take_part_in/5             %% 离线托管：将离线玩家拉上线参与活动

    , check_can_host/3           %% 判断能否参与托管

    , notice_logout/1            %% 离线托管：活动结束, 将指定玩家踢下线
    , notice_logout/2            %% 离线托管：活动结束, 将所有玩家踢下线
    , notice_logout/3            %% 离线托管：活动结束, 将所有玩家踢下线

    , stop_behavior/1            %% 停止托管行为

    , cancel_behavior_timer/1    %% 取消旧行为定时器
    , host_behavior_delay/4      %% 延迟执行某一行为

    , host_behavior_continue/5   %% 行为：登陆
    , host_behavior_login/5      %% 行为：登陆
    , host_behavior_load_scene/3 %% 行为：加载场景
    , host_behavior_move/6       %% 行为：移动
    , host_behavior_battle/1     %% 行为：原地战斗
    , host_behavior_battle/2
    , host_behavior_stop_battle/1%% 行为：停止战斗
    , host_behavior_die/1        %% 行为：死亡
    , host_behavior_reward/2     %% 行为：获得奖励
    , host_behavior_logout/3     %% 行为：退出

    , host_behavior/3            %% 通用行为处理
]).

%% API
-export([
    start_link/0,
    clean_log/1,
    get_handler/1
]).

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

%% 有些活动是派假人参与，真实玩家可以进行其他操作，不受影响
-define(OFFLINE_HOSTING_DUMMY, [
    ?MOD_WASTELAND_WAR      %% 废土战场(饥饿游戏)
]).

%% 是否派出假人参与活动
is_send_dummy_act(Mod) ->
    lists:member(Mod, ?OFFLINE_HOSTING_DUMMY).

%% 离线托管：将离线玩家拉上线参与活动
%% Handler 逻辑处理模块
%% Mod 功能id
%% SubMod 子id 默认为1
%% RoleId 玩家id
%% Arg 初始化参数
%% DelayTime 延迟多长时间后才开始拉人
%% 拉所有玩家进入玩法：每200毫秒1个人
take_all_part_in(Handler, Mod, Arg, DelayTime) ->
    Pid = spawn(
        fun() ->
            is_integer(DelayTime) andalso timer:sleep(DelayTime),
            Fun = ets:fun2ms(fun(EtsHosting) when EtsHosting#ets_offline_hosting.value> 0 -> EtsHosting#ets_offline_hosting.role_id end),
            AllHostId = ets:select(?ETS_OFFLINE_HOSTING, Fun),
            take_all_part_in_each(AllHostId, 0, Handler, Mod, Arg)
        end
    ),
    lib_server_kv:update_server_kv_ets(#server_kv{key = {?SERVER_KV_HOSTING_PROCESS, Mod}, value = Pid}),
    Pid.

take_all_part_in_each(_, Num, _Handler, _Mod, _Arg) when Num >= ?OFFLINE_HOSTING_LIMIT ->
    ok;
take_all_part_in_each([], _Num, _Handler, _Mod, _Arg) ->
    ok;
take_all_part_in_each([RoleId|T], Num, Handler, Mod, Arg) ->
    case take_part_in(Handler, Mod, RoleId, Arg) of
        1  ->
            timer:sleep(200),
            take_all_part_in_each(T, Num + 1, Handler, Mod, Arg);
        _  ->
            take_all_part_in_each(T, Num, Handler, Mod, Arg)
    end.

%% 拉某个玩家上线进入玩法
take_part_in(Handler, Mod, RoleId, Arg) ->
    take_part_in(Handler, Mod, 1, RoleId, Arg).
take_part_in(Handler, Mod, SubMod, RoleId, Arg) ->
    case check_can_host(RoleId, Mod, SubMod) of
        {true,  AccId, AccName} ->
            %% 如果玩家的镜像正在参与托管，则停止镜像的托管
            case offline_hosting_data:hosting_role_lookup(RoleId) of
                [#ets_hosting_role{mon_id = MonId, pid = MonPid}] ->
                    case MonId > 0 of
                        true  ->
                            offline_hosting_dummy:notice_logout(RoleId, MonPid, MonId);
                        false ->
                            ok
                    end;
                _ ->
                    ok
            end,
            OnlinePid = misc:get_player_process(RoleId),
            case misc:is_process_alive(OnlinePid) of
                true  ->
                    %% 执行上线行为，玩家已经在线了，还需要判断玩家是否正处于托管状态，如果处于托管状态则可以继续让玩家参与新的玩法
                    %% 如果处于正常在线状态，会忽略掉
                    case catch mod_server:online_flag(OnlinePid) of
                        ?ONLINE_OFF_HOSTING ->
                            erlang:send(OnlinePid, {mod, ?MODULE, host_behavior_continue, [Handler, Mod, SubMod, Arg]}),
                            1;
                        _ ->
                            -1
                    end;
                false ->
                    case is_send_dummy_act(Mod) of
                        true  ->
                            offline_hosting_dummy:take_part_in(Handler, Mod, SubMod, RoleId, Arg),
                            1;
                        false ->
                            ConnPid = spawn(fun() -> ok end),   % 构造一个过时的Pid
                            LoginParams = #login_params{
                                id = RoleId, accid = AccId, accname = AccName, ip = <<>>,
                                socket = none, ids = [], network_type = 0,
                                login_source = <<>>, ta_guest_id = <<>>
                            },
                            case catch mod_server:start([ConnPid, ?ONLINE_OFF_HOSTING, LoginParams]) of
                                {ok, Pid} ->
                                    %% 执行上线行为
                                    erlang:send(Pid, {mod, ?MODULE, host_behavior_login, [Handler, Mod, SubMod, Arg]}),
                                    1;
                                _R ->
                                    ?ERR("~p", [_R]),
                                    0
                            end
                    end
            end;
        Res ->
            Res
    end.

%% 判断能否参与托管
check_can_host(RoleId, Mod, SubMod) ->
    case offline_hosting_data:host_setting_lookup(RoleId) of
        #ets_offline_hosting{
            close_list  = SetList,       %% 设置忽略列表
            value       = Value          %% 当前托管值
        } ->
            CostValue = data_offline_hosting:get_offline_hosting_value(Mod, SubMod),
            IsSetClose = lists:member({Mod, SubMod}, SetList),
            if
                not is_integer(CostValue) -> 3;        %% 配置不正常，不处理
                Value < CostValue -> 4;                %% 托管值不足，不处理
                IsSetClose -> 5;                       %% 没有设置可托管，不处理
                true ->
                    case lib_role:get_role_show(RoleId) of
                        #ets_role_show{
                            accid = AccId,
                            accname = AccName,
                            last_logout_time = LastLogoutTime,
                            figure = #figure{lv = Lv}
                        } = RoleShow ->
                            Now = utime:unixtime(),
                            LeaveTime = Now - LastLogoutTime,
                            OfflineDay = data_offline_hosting:get_offline_hosting_kv(ai_day),
                            NeedLv = lib_module:get_open_lv(?MOD_OFFLINE_HOSTING),
                            if
                                LeaveTime > OfflineDay * 86400 -> 6;   %% 离线太久，不处理
                                Lv < NeedLv -> 10;                     %% 等级不足，不处理
                                true ->
                                    case check_part_in(Mod, RoleShow) of
                                        true  ->
                                            {true, AccId, AccName};
                                        false ->
                                            7
                                    end
                            end;
                        _ ->
                            8
                    end
            end;
        _ ->
            %% 没有找到玩家
            9
    end.

%% 离线托管：活动结束, 将玩家踢下线
%% ModId 功能id
%% 延迟5秒踢玩家下线，防止一些相邻的玩法出现拉人和踢人同时处理的情况
notice_logout(ModId) ->
    notice_logout(ModId, 1).
notice_logout(ModId, SubId) ->
    %% 先停止拉托管玩家的进程
    Key = {?SERVER_KV_HOSTING_PROCESS, ModId},
    [{_, HostingPid}] = lib_server_kv:get_value([{Key, undefined}]),
    misc:is_process_alive(HostingPid) andalso erlang:exit(HostingPid, kill),
    lib_server_kv:delete_server_kv_ets(Key),
    
    spawn(
        fun() ->
            timer:sleep(5000),
            All = ets:tab2list(?ETS_HOSTING_ROLE),
            [
                begin
                    case MonId > 0 of
                        false ->
                            erlang:send(Pid, {mod, ?MODULE, host_behavior_logout, [ModId, SubId]});
                        true ->
                            offline_hosting_dummy:notice_logout(RoleId, Pid, MonId)
                    end,
                    timer:sleep(100)
                end || #ets_hosting_role{
                            pid = Pid,
                            mon_id = MonId,
                            role_id = RoleId
                        } <- All
            ]
        end
    ).

notice_logout(ModId, SubId, RoleId) ->
    case ets:lookup(?ETS_HOSTING_ROLE, RoleId) of
        [#ets_hosting_role{role_id = RoleId, pid = Pid, mon_id = MonId}] ->
            case MonId > 0 of
                false ->
                    erlang:send(Pid, {mod, ?MODULE, host_behavior_logout, [ModId, SubId]});
                true ->
                    offline_hosting_dummy:notice_logout(RoleId, Pid, MonId)
            end;
        _ ->
            ok
    end.

%% 停止托管行为
stop_behavior(Player) ->
    case offline_hosting_api:is_offline_hosting(Player) of
        true  -> offline_hosting:cancel_behavior_timer(Player);
        false -> {ok, Player}
    end.

%% 获取托管代理模块
get_handler(Player) -> Player#player_status.offline_hosting#offline_hosting.handler.

%% 取消旧行为定时器
cancel_behavior_timer(Player) ->
    OffHost = Player#player_status.offline_hosting,
    util:cancel_timer(OffHost#offline_hosting.behavior_ref),
    host_behavior_stop_battle(Player),
    {ok, Player#player_status{offline_hosting = OffHost#offline_hosting{behavior_ref = undefined}}}.

%% 延迟执行某一行为
%% NextBehavior
%% BehaviorArg [term()]
%% DelayTime ms
host_behavior_delay(#player_status{online = ?ONLINE_OFF_HOSTING} = Player, NextBehavior, BehaviorArg, 0) ->
    erlang:send(self(), {offline_hosting_behavior, NextBehavior, BehaviorArg}),
    {ok, Player};
host_behavior_delay(#player_status{online = ?ONLINE_OFF_HOSTING} = Player, NextBehavior, BehaviorArg, DelayTime) ->
    Ref = set_timer(DelayTime, NextBehavior, BehaviorArg),
    OffHost = Player#player_status.offline_hosting,
    util:cancel_timer(OffHost#offline_hosting.behavior_ref),
    {ok, Player#player_status{offline_hosting = OffHost#offline_hosting{behavior_ref = Ref}}};
host_behavior_delay(Player, _, _, _) -> {ok, Player}.

set_timer(DelayTime, NextBehavior, BehaviorArg) ->
    erlang:send_after(DelayTime, self(), {offline_hosting_behavior, NextBehavior, BehaviorArg}).

%%%-------------------------------------------------------------------
%%% @doc
%%%     托管期间基本行为
%%% @end
%%%-------------------------------------------------------------------
%% 活动参与条件判断
check_part_in(?MOD_MADNESS_LAND, RoleShow) ->
    offline_hosting_madness_land:check_enter(RoleShow);
check_part_in(_Mod, _RoleShow) ->
    true.

%% 玩家已经登陆，继续行为
host_behavior_continue(Player, Handler, Mod, SubMod, Arg) ->
    host_behavior('CONTINUE', Player, [Handler, Mod, SubMod, Arg]).

%% 登陆行为: 托管登陆完成
host_behavior_login(Player, Handler, Mod, SubMod, Arg) ->
    host_behavior('LOGIN', Player, [Handler, Mod, SubMod, Arg]).

%% 行为：加载场景
host_behavior_load_scene(Player, Arg, DelayTime) ->
    case DelayTime > 0 of
        true  ->
            host_behavior_delay(Player, 'LOAD_SCENE', Arg, DelayTime);
        false ->
            host_behavior('LOAD_SCENE', Player, Arg)
    end.
    
%% 移动行为：需要指定移动到目的地后的行为
%% DestinationX 目的地坐标
%% DestinationY 目的地坐标
%% DestinationBehavior 到达目的地后执行的行为
%% DestinationArg   到达目的地后执行的行为参数
%% ArriveDelayTime  到达目的地后行为延迟时间
host_behavior_move(Player, DestinationX, DestinationY, DestinationBehavior, DestinationArg, ArriveDelayTime) ->
    Arg = [DestinationX, DestinationY, DestinationBehavior, DestinationArg, ArriveDelayTime],
    host_behavior('MOVE', Player, Arg).

%% 原地战斗
host_behavior_battle(Player) ->
    host_behavior_battle(Player, 1).
%% FindType:1怪优先|2人优先|3只找怪
host_behavior_battle(Player, FindType) ->
    host_behavior('ATTACK', Player, [FindType]).
%% 停止战斗
host_behavior_stop_battle(Player) ->
    host_behavior('STOP_ATTACK', Player, []).

%% 玩家死亡
host_behavior_die(Player) ->
    host_behavior('DIE', Player, []).

%% 离线托管中获得一份奖励: 内存记录，下线时保存
%% lib_goods_api:send_reward
%% lib_goods_api:send_reward_with_mail
host_behavior_reward(RoleId, Award) when is_integer(RoleId) ->
    offline_hosting_data:hosting_log(RoleId, Award);
host_behavior_reward(Player, Award) ->
    offline_hosting_data:hosting_log(Player#player_status.id, Award),
    {ok, Player}.

%% 活动结束
host_behavior_logout(Player, ModId, SubId) ->
    host_behavior('LOGOUT', Player, [ModId, SubId]).

%%%-------------------------------------------------------------------
%%% @doc
%%%     行为执行并切换处理
%%% @end
%%%-------------------------------------------------------------------
%% 这些行为全部只在托管状态才生效
host_behavior(_Any, #player_status{online = Online} = Player, _Arg) when Online =/= ?ONLINE_OFF_HOSTING ->
    {ok, Player};

%% 玩家已经登陆正在参与其他玩法，继续行为
host_behavior('CONTINUE', Player, [Handler, Mod, SubMod, Arg]) ->
    #player_status{
        id = RoleId,
        offline_hosting = Hosting
    } = Player,
    util:cancel_timer(Hosting#offline_hosting.behavior_ref),
    case offline_hosting_data:hosting_role_is_hosting(RoleId) of
        true ->
            %% 当前有玩法没有结算，则先结束当前玩法，再参与其他玩法
            %% !! 对于延迟发奖的玩法，奖励记录无法正确记录到该玩法中了
            {ok, Player1} = host_behavior('END', Player, []);
        false ->
            Player1 = Player
    end,
    case is_send_dummy_act(Mod) of
        true  ->
            %% 派出假人，参与活动，自己就退出
            offline_hosting_dummy:take_part_in(Handler, Mod, SubMod, RoleId, Arg),
            host_behavior_logout(Player1, Mod, SubMod);
        false ->
            host_behavior('LOGIN', Player1, [Handler, Mod, SubMod, Arg])
    end;
%% 登陆行为: 托管登陆完成
%% 防止上线的时候是死亡状态
host_behavior('LOGIN', Player, [Handler, Mod, SubMod, Arg]) ->
    #player_status{
        id = RoleId,
        offline_hosting = Hosting,
        battle_attr = #battle_attr{hp_lim = Hp} = BA
    } = Player,
    OfflineHosting = Hosting#offline_hosting{
        handler = Handler,
        mod_id = Mod
    },
    Player1 = Player#player_status{
        offline_hosting = OfflineHosting,
        battle_attr = BA#battle_attr{hp = Hp},
        act_label = []
    },
    %% 如果玩家还在参与其他活动，那就直接解锁，正常情况下不会同时开启两个需要托管的玩法
    %% 所以这里直接用后一个玩法取代前一个玩法
    offline_hosting_data:hosting_role_save(
        #ets_hosting_role{
            role_id = RoleId,
            mod = Mod,
            sub_id = SubMod,
            pid = self(),
            time = utime:unixtime()
        }
    ),
    %% 1分钟后扣除托管值,玩家如果及时上线，那就不会扣除托管值
    set_timer(60000, 'DEDUCT', [Mod, SubMod]),
    host_behavior(login, Player1, Arg);

host_behavior(Any, #player_status{offline_hosting = #offline_hosting{handler = undefined}} = Player, Arg) ->
    ?ERR("missing handler![~p, ~p]", [Any, Arg]),
    {ok, Player};

%% 加载场景：如果玩家不在目标场景，那么直接踢玩家下线
host_behavior('LOAD_SCENE', Player, Arg) ->
    case is_in_act_scene(Player) of
        true  ->
            {ok, Player1} = pp_scene:handle(12002, Player, []),
            host_behavior(load_scene, Player1, Arg);
        false ->
            mod_server:cast_stop(Player#player_status.pid, ?LOGOUT_LOG_HOSTING)
    end;

%% 延迟扣除托管值
%% 加一个条件判断
host_behavior('DEDUCT', Player, [Mod, SubMod]) ->
    #player_status{id = RoleId} = Player,
    case offline_hosting_data:hosting_role_lookup(RoleId) of
        [#ets_hosting_role{mod = Mod, sub_id = SubMod, cost_value = undefined} = RoleInfo] ->
            Cost = offline_hosting_data:host_value_cost(RoleId, Mod, SubMod),
            NewRoleInfo = RoleInfo#ets_hosting_role{cost_value = Cost},
            offline_hosting_data:hosting_role_save(NewRoleInfo);
        _ ->
            ok
    end,
    {ok, Player};

%% 移动行为：需要指定移动到目的地后的行为
%% DestinationX 目的地坐标
%% DestinationY 目的地坐标
%% DestinationBehavior 到达目的地后执行的行为
%% DestinationArg   到达目的地后执行的行为参数
%% ArriveDelayTime  到达目的地后行为延迟时间
host_behavior('MOVE', Player, [DestinationX, DestinationY, DestinationBehavior, DestinationArg, ArriveDelayTime] = Arg) ->
    #player_status{
        scene = SceneId,
        copy_id = CopyId,
        x = OX,
        y = OY
    } = Player,
    case lib_scene_object_ai:get_next_step(OX, OY, 300, SceneId, CopyId, DestinationX, DestinationY, false) of
        attack ->
            host_behavior_delay(Player, DestinationBehavior, DestinationArg, ArriveDelayTime);
        {Next_X, Next_Y} ->
            mod_scene_agent:move(Next_X, Next_Y, Next_X, Next_Y, 0, Next_X, Next_Y, Player),
            Player1 = Player#player_status{x = Next_X, y = Next_Y},
            Dis = umath:distance({OX, OY}, {Next_X, Next_Y}),
            Speed = Player1#player_status.battle_attr#battle_attr.speed,
            Time = case Dis =:= 0 of
                       true  -> 1000;
                       false -> round(Dis * 1000 / Speed)
                   end,
            host_behavior_delay(Player1, 'MOVE', Arg, Time)
    end;

%% 原地攻击 FindType:1怪优先|2人优先|3只找怪
host_behavior('ATTACK', Player, [FindType]) ->
    #player_status{scene = SceneId, scene_pool_id = ScenePoolId, id = RoleId} = Player,
    mod_scene_agent:apply_cast_with_scene(SceneId, ScenePoolId, offline_hosting_battle, battle, [RoleId, FindType]),
    {ok, Player};

%% 停止战斗
host_behavior('STOP_ATTACK', Player, []) ->
    #player_status{scene = SceneId, scene_pool_id = ScenePoolId, id = RoleId} = Player,
    mod_scene_agent:apply_cast(SceneId, ScenePoolId, offline_hosting_battle, stop_battle, [RoleId]),
    {ok, Player};

%% 玩家死亡
host_behavior('DIE', Player, Arg) ->
    {ok, Player1} = cancel_behavior_timer(Player),
    host_behavior(die, Player1, Arg);

%% 活动结束，踢人
host_behavior('LOGOUT', Player, [ModId, SubId]) ->
    #player_status{
        id = RoleId,
        pid = Pid
    } = Player,
    case offline_hosting_data:hosting_role_lookup(RoleId) of
        [#ets_hosting_role{mod = ModId,sub_id = SubId}] ->
            mod_server:cast_stop(Pid, ?LOGOUT_LOG_HOSTING);
        _ ->
            %% 不是当前玩法，可能是玩家去参与其他玩法了，那就不走结算了
            ok
    end,
    {ok, Player};
%% 结束托管
host_behavior('END', Player, _Arg) ->
    #player_status{
        id = RoleId,
        figure = Figure,
        offline_hosting = Hosting
    } = Player,
    %% 结束托管预处理
    {ok, Player1} = host_behavior(host_end_pre, Player, _Arg),
    
    util:cancel_timer(Hosting#offline_hosting.behavior_ref),
    
    OneLog = offline_hosting_data:hosting_log_save_db(Figure),
    Hosting1 = Hosting#offline_hosting{log = OneLog ++ Hosting#offline_hosting.log},
    Player2 = Player1#player_status{offline_hosting = Hosting1},
    
    {ok, Player3} = host_behavior(host_end, Player2, _Arg),
    #player_status{offline_hosting = Hosting2} = Player3,
    Hosting3 = Hosting2#offline_hosting{handler = undefined, mod_id = 0},
    offline_hosting_data:hosting_role_delete(RoleId),
    {ok, Player3#player_status{offline_hosting = Hosting3}};

%% 玩家执行某行为
%% Arg [term()]
host_behavior(Behavior, Player, Arg) ->
    Handler = get_handler(Player),
    apply(Handler, host_behavior, [Behavior, Player, Arg]).

%%--------------------------------------------------------------------
%% @doc
%%     一些离线托管玩家的数据管理 托管日志只保留最近一个月的/ETS表中，定时清理托管值为0的玩家
%% @end
%%--------------------------------------------------------------------
%% 删除一个月前的托管日志
clean_log(7) ->
    ?MODULE ! clean,
    ?MODULE ! clean_ets;
clean_log(_) -> ok.

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

init([]) ->
    ets:new(?ETS_OFFLINE_HOSTING,  [{keypos, #ets_offline_hosting.role_id}, named_table, public, set]), % 离线托管设置
    ets:new(?ETS_HOSTING_ROLE,     [{keypos, #ets_hosting_role.role_id}, named_table, public, set]),    % 正在离线托管
    %% 5秒后加载所有玩家的离线托管设置
    erlang:send_after(5000, self(), load_host_setting),
    {ok, []}.

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

handle_cast(_Msg, State) ->
    {noreply, State}.

%% 加载所有玩家的离线托管设置
handle_info(load_host_setting, State) ->
    offline_hosting_data:load_all_host_setting(),
    {noreply, State};
%% 清理日志
handle_info(clean, State) ->
    offline_hosting_data:delete_long_time_ago_log(),
    offline_hosting_data:delete_long_time_ago_role(),
    {noreply, State};
%% 清理内存中的玩家设置
handle_info(clean_ets, State) ->
    offline_hosting_data:host_clean_ets(),
    {noreply, State};
handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

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

%% 是否在活动场景，当前开发的玩法都是在特殊活动场景，所以认为玩家如果在野外场景，那就是不正常，直接踢下线吧
%% 未来如果有玩法需要在野外场景进行托管，则需要修改该方法。也很容易发现，因为托管玩家会参与不了玩法!!!
is_in_act_scene(Player) ->
    not lib_scene:is_outside_scene(Player#player_status.scene).
