%%--------------------------------------------------------------------
%% @create yxf
%% @date 20171211
%% @title 魅力海滩
%%--------------------------------------------------------------------
-module(lib_charm_beach).
-include("errcode.hrl").
-include("charm_beach.hrl").
-include("scene.hrl").
-include("common.hrl").
-include("def_fun.hrl").
-include("def_module.hrl").
-include("server.hrl").
-include("predefine.hrl").
-include("figure.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").
-include("clusters.hrl").
-include("attr.hrl").
-compile(export_all).


%% 活动相关(游戏服控制托管)
act_start(AcSub) ->
    case config:get_cls_type() of
        ?NODE_CENTER ->
            ignore;
        _ ->
            mod_charm_beach:start_hosting()
    end,
    mod_charm_beach:act_start(AcSub),
    ok.
    %end.

act_ready(Time) ->
    lib_chat:send_TV({all}, ?MOD_BEACH, 1, [Time]),
    mod_charm_beach:act_ready(Time).

%% @return {Stage, ReadyTime, TimerSeconds, StartTime}
get_next_check_time(TimeOffset, Start, End) ->
    UnixTime = utime:unixtime() + TimeOffset,
    Reward = End - data_charm_beach:get_cfg(reward_to_over_time),
    if
        UnixTime >= Start andalso UnixTime < Reward ->
            {?BEACH_OPEN, Reward - UnixTime};
        UnixTime >= Reward andalso UnixTime < End ->
            {?BEACH_REWARD, End - UnixTime};
        true ->
            {?BEACH_CLOSE, 0}
    end.

get_next_solo_time(TimeOffset, Start, End) ->
    UnixTime = utime:unixtime() + TimeOffset,
    Reward = End - data_charm_beach:get_cfg(reward_to_over_time),
    if
        UnixTime >= Start andalso UnixTime < Reward ->
            Interval = data_charm_beach:get_cfg(solo_interval),
            Interval;
        true ->
            0
    end.

%% 获取场景出生点
get_scene_born_xy(SceneId) ->
    case data_charm_beach:get_cfg(born_loc) of
        [{_X, _Y, _Radius} | _] = BornLoc ->
            case urand:list_rand(BornLoc) of
                {X, Y, Radius} -> umath:rand_point_in_circle(X, Y, Radius, 1); %% 获取圆内随机一点
                _ -> do_get_scene_born_xy(SceneId)
            end;
        _ -> do_get_scene_born_xy(SceneId)
    end.

%% 获取氛围假人出生点
get_dummy_born_xy(SceneId) ->
    case data_charm_beach:get_cfg(born_loc) of
        [{_X, _Y, _Radius} | _] = BornLoc ->
            case urand:list_rand(BornLoc) of
                {X, Y, Radius} -> umath:rand_point_in_circle(X, Y, Radius, 1); %% 获取圆内随机一点
                _ -> do_get_scene_born_xy(SceneId)
            end;
        _ -> do_get_scene_born_xy(SceneId)
    end.

do_get_scene_born_xy(SceneId) ->
    [X, Y] = lib_scene:get_born_xy(SceneId),
    {X, Y}.

%% 创建假人
create_dummy(Scene, PoolId) ->
    DummyList = data_charm_beach:get_all_dummy(),
	%% 创建假人
    Lv = 100 + urand:rand(0,30), Turn = 0, Career = urand:rand(1,4),
    F = fun(DummyId) ->
        case data_charm_beach:get_dummy_cfg(DummyId) of
            #base_beach_dummy{sex = Sex, location = {BornX, BornY}, model = LvModel, fashion_model = FashionModel, name = Name} ->
                % Figure = lib_dummy_api:change_figure(#figure{lv = Lv, sex = Sex, career = Career, lv_model =  LvModel}, [rand_fashion]
                % ), %% TODO
                Figure =#figure{lv = Lv, sex = Sex, name = Name, career = Career, turn = Turn, lv_model =  LvModel, fashion_model = FashionModel},
                lib_scene_object:async_create_a_dummy(Scene, PoolId, BornX, BornY, 0, 1, Figure, #battle_attr{attr = #attr{speed = 0}}, [{dummy, #scene_dummy{is_dance = 1}}]);
            _ ->
                ignore
        end end,
    lists:foreach(F, DummyList),
    ok.

is_in_scene(SceneId, ActType) ->
    SceneId == ?SCENE_ID(ActType).

%% 切换场景
change_player_scene(RoleId, SceneId, ScenePool, X, Y) ->
    lib_player:apply_cast(RoleId, ?APPLY_CALL_SAVE, ?NOT_HAND_OFFLINE, ?MODULE, change_player_scene1, [RoleId, SceneId, ScenePool, X, Y]).

change_player_scene1(Player, _RoleId, SceneId, ScenePool, X, Y) ->
    NewPlayer = lib_scene:change_scene(Player, SceneId, ScenePool, 0, X, Y, true, false, []),
    {ok, NewPlayer}.

%% 跨服中心给玩家发协议
send_to_uid(Node, RoleId, BinData) when BinData =/= [] ->
    case Node =:= [] of
        true ->
            lib_server_send:send_to_uid(RoleId, BinData);
        false ->
            clusters_api:send_to_uid(Node, RoleId, BinData)
    end;
send_to_uid(_Node, _RoleId, _Bin) -> skip.

%% 获取每5秒增加的经验 直接读配置 (原公式:等级基础*倍率*VIP加成)
get_add_exp(_ModuleId, _SubModule, Lv, _VipType, VipLv) ->
    case data_charm_beach:get_exp_cfg(Lv) of
        #base_beach_exp{timer_add_exp = BaseExp} ->
            VipRatio = case VipLv >= 4 of
                           true ->
                               data_charm_beach:get_cfg(vip_add);
                           _ -> 0
                       end,
            {BaseExp, VipRatio};
        _ -> {0, 0}
    end.
%%    case data_exp_base:get(Lv) of
%%        BaseExp when is_integer(BaseExp) andalso BaseExp > 0 ->
%%            ExpRatio = case data_award_exp:get_exp_extra(ModuleId, SubModule, Lv) of
%%                           Ratio when is_integer(Ratio) andalso Ratio > 0 -> Ratio;
%%                           _ -> 0
%%                       end,
%%            VipRatio = case VipLv >= 4 of
%%                           true ->
%%                               data_charm_beach:get_cfg(vip_add);
%%                           _ -> 0
%%                       end,
%%            FRatio = (ExpRatio / 10000) * (1 + VipRatio/100),
%%            NewBaseExp = trunc(BaseExp * FRatio),
%%            NewBaseExp;
%%        _ ->
%%            0
%%    end.

%% 投票增加经验 公式:等级基础*倍率*投票倍率
get_vote_add_exp(_ModuleId, _SubModule, Lv) ->
    case data_charm_beach:get_exp_cfg(Lv) of
        #base_beach_exp{vote_add_exp = AddExp} ->
            AddExp;
        _ -> 0
    end.
%%    case data_exp_base:get(Lv) of
%%        BaseExp when is_integer(BaseExp) andalso BaseExp > 0 ->
%%            ExtraRatio = case data_award_exp:get_exp_extra(ModuleId, SubModule, Lv) of
%%                               Ratio when is_integer(Ratio) andalso Ratio > 0 -> Ratio;
%%                               _ -> 0
%%                           end,
%%            ExpRatio = data_charm_beach:get_cfg(vote_exp_ratio),
%%            FRatio = (ExtraRatio / 10000) * (ExpRatio / 10000),
%%            NewBaseExp = trunc(BaseExp * FRatio),
%%            NewBaseExp;
%%        _ ->
%%            0
%%    end.

%% 判断活动是本服的还是跨服的
get_act_cls_type() ->
    case data_charm_beach:get_cfg(open_day_to_kf) of
        KFMinDay when is_integer(KFMinDay) ->
            Opday = util:get_open_day(),
            case Opday >= KFMinDay of
                true ->
                    %% 需要判断是否进入跨服节奏
                    case check_act_type() of
                        1 -> ?ACT_TYPE_KF;
                        _ -> ?ACT_TYPE_BF
                    end;
                false ->
                    ?ACT_TYPE_BF
            end;
        _ ->
            ?ACT_TYPE_BF
    end.

check_act_type() ->
    case czone_api:zone_open_check() of
        {true, ZoneId} when is_integer(ZoneId) andalso ZoneId > 0 ->
            1;
        _ ->
            0
    end.

leave_beach(RoleId, SceneId, PoolId, CopyId, X, Y) ->
    ActType = get_act_cls_type(),
    case is_in_scene(SceneId, ActType) of
        true ->
            Node = clusters_node:get_node(),
            if
                ActType =:= ?ACT_TYPE_KF ->
                    clusters_node:apply_cast(mod_charm_beach, quit_scene, [Node, RoleId, SceneId, PoolId, CopyId, X, Y]);
                true ->
                    mod_charm_beach:quit_scene(Node, RoleId, SceneId, PoolId, CopyId, X, Y)
            end;
        _ ->
            skip
    end.

reconnect(PS, LoginType) ->
    #player_status{scene = SceneId, id = RoleId, pid = Pid,
        server_id = ServerId, server_num = ServerNum, figure = Figure} = PS,
    case lists:member(SceneId, ?BEACH_SCENE_ID) of
        true ->
            if
                LoginType =:= ?RE_LOGIN ->
                    ActType = get_act_cls_type(),
                    Node = clusters_node:get_node(),
                    IsHosting = 
                    case offline_hosting_api:is_offline_hosting(PS) of
                        true -> 1;
                        _ -> 0
                    end,
                    if
                        ActType =:= ?ACT_TYPE_KF ->
                            RoleMsg = {RoleId, Pid, ServerId, ServerNum, 0, IsHosting},
                            clusters_node:apply_cast(mod_charm_beach, reconnect, [Node, RoleMsg, Figure]);
                        true ->
                            RoleMsg = {RoleId, Pid, ServerId, ServerNum, 0, IsHosting},
                            mod_charm_beach:reconnect(Node, RoleMsg, Figure)
                    end,
                    {ok, PS};
                true ->
                    NewPS = lib_scene:change_default_scene(PS, []),
                    {ok, NewPS}
            end;
        _ ->
            {next, PS}
    end.


%% 玩家自己
rank_format(_RoleId, [], MyRank) -> MyRank;
rank_format(RoleId, RankList, MyRank) ->
	case lists:keyfind(RoleId, 1, RankList) of
		{_, [Rank, CharmValue]} -> skip;
		_ -> Rank = 0, CharmValue = 0
	end,
    NMyRank = [{Rank, CharmValue}|MyRank],
	NMyRank.

%% 打包排行榜数据
rank_format(Members, RoleId, VoteIds, RankList, Length) ->
    MaxNum = data_charm_beach:get_cfg(list_max),
    rank_format(Members, RoleId, VoteIds, RankList, {[], []}, Length, MaxNum).

%% 排名前三(打包完整形象)
rank_format(Members, RoleId, VoteIds, [{TRoleId, [Rank, CharmValue]}|T], {TopRank, LeftRank}, Length, Max) when Rank =< 3 ->
    case maps:get(TRoleId, Members, false) of
        #beach_member{figure = Figure, server_num = ServerNum} -> skip;
        _ -> Figure = #figure{}, ServerNum = 1
    end,
    case lists:member(TRoleId, VoteIds) of
        true ->
            NTopRank = [{Rank, CharmValue, Figure, ServerNum, 1}|TopRank];
        _ ->
            NTopRank = [{Rank, CharmValue ,Figure, ServerNum, 0}|TopRank]
    end,
    NewPack = {NTopRank, LeftRank},
    rank_format(Members, RoleId, VoteIds, T, NewPack, Length, Max);
%% 排名第三之后(只打包头像),分页，每次请求20条
rank_format(Members, RoleId, VoteIds, [{TRoleId, [Rank, CharmValue]}|T], {TopRank, LeftRank}, Length, Max)
    when Rank =< Max andalso Rank >= Length+1 andalso Rank =< (Length + 20) andalso Rank > 3 ->
    case maps:get(TRoleId, Members, false) of
        #beach_member{figure = Figure, server_num = ServerNum} -> skip;
        _ -> Figure = #figure{}, ServerNum = 1
    end,
    case lists:member(TRoleId, VoteIds) of
        true ->
            NLeftRank = [{Rank, CharmValue, Figure, ServerNum, 1}|LeftRank];
        _ ->
            NLeftRank = [{Rank, CharmValue, Figure, ServerNum, 0}|LeftRank]
    end,
    NewPack = {TopRank, NLeftRank},
    rank_format(Members, RoleId, VoteIds, T, NewPack, Length, Max);
rank_format(Members, RoleId, VoteIds, [_|T], NewPack, Length, Max) ->
    rank_format(Members, RoleId, VoteIds, T, NewPack, Length, Max);
rank_format(_, _, _, [], NewPack, _, _) -> NewPack.


%% 更新投票榜
insert_rank_list(RankList, Mem) when is_record(Mem, beach_member) ->
    #beach_member{
        role_id = RoleId,
        charm_value = CharmValue
    } = Mem,
    insert_rank_list(RankList, RoleId, CharmValue).

insert_rank_list(RankList, RoleId, CharmValue) ->
    F1 = fun({_, [_, CharmV1|_]}, {_, [_, CharmV2|_]}) ->
        CharmV1 > CharmV2
    end,
    {_, NewRankList} = ulists:sorted_insert(F1, {RoleId, [0, CharmValue]}, lists:keydelete(RoleId, 1, RankList)),
    update_rank(NewRankList, 0, []).

update_rank([], _Rank, NewRankList) -> NewRankList;
update_rank([RankInfo|List], Rank, TRankList) ->
    {RoleId, [_, CharmV]} = RankInfo,
    NewRankL = lists:keystore(RoleId, 1, TRankList, {RoleId, [Rank+1, CharmV]}),
    update_rank(List, Rank+1, NewRankL).

%% 根据角色Id获取排行
get_rank_by_roleid(RoleId, RankList) ->
    get_rank_by_roleid(RoleId, RankList, 0).

get_rank_by_roleid(RoleId, [{RoleId, [Rank|_]}|_], _TRank) -> Rank;
get_rank_by_roleid(RoleId, [{TRoleId, [_Rank|_]}|T], _TRank) when TRoleId =/= RoleId ->
    get_rank_by_roleid(RoleId, T, _TRank);
get_rank_by_roleid(_RoleId, [], Rank) -> Rank.

%% 更新进场景玩家的分线信息
update_role_scene_pool(RoleId, ScenePoolId, RoleKeyMaps) ->
    case maps:get(ScenePoolId, RoleKeyMaps, []) of
        Ids = [_|_] ->
            case lists:member(RoleId, Ids) of
                true ->
                    RoleKeyMaps;
                _    ->
                    NewIds = [RoleId|Ids],
                    maps:put(ScenePoolId, NewIds, RoleKeyMaps)
            end;
        _ ->
            maps:put(ScenePoolId, [RoleId], RoleKeyMaps)
    end.

update_room_maps(ScenePool, ServerId, Node, RoomMaps) ->
    case maps:get(ScenePool, RoomMaps, false) of
        RoomRes = [_|_] ->
            NewRoomRes = case lists:member({ServerId, Node}, RoomRes) of
                             true -> RoomRes;
                             _    -> [{ServerId, Node}|RoomRes]
                         end,
            maps:put(ScenePool, NewRoomRes, RoomMaps);
        _ ->
            maps:put(ScenePool, [{ServerId, Node}], RoomMaps)
    end.

%% 获得房间说明
%% 原则:
%%  跨服玩法中：
%%  1)房间分区由开服时间决定,每次活动重新获得分区信息(每4个服一个房间(分区))，分区下按策划配置人数进行分线
%%  2)如果活动期间还有新游戏服连入跨服,暂不处理,等下次活动再次进行分区
%%  本服玩法中：
%%  1)不进行分区,直接按策划配置进行分线
%%  通用设定:
%%  1)目前:满120人开新分线,一个房间人数不限制
%%  2)重新进入玩家尽量回到原来的房间中,原来的线满了选择其他线
%%  详细规则:
%%  先检测玩家所在服有无在room_maps中
%%  (1)若已分配房间,则检测房间中有无未满的线路,人数满了则给这个房间开新分线;若有线路人数未满则分配给玩家(分配人数少的线路)
%%  (2)还未分配房间,则说明可能是新服,暂时先不处理活动期间才加入的游戏服
%%  备注:
%%  线路一定要在确定房间后在检测,并且线路和房间绑定,只要进了一个房间,玩家可以挑选任意线路,

% ServerId, Node, CurRoom, CurLine, RoomLines, RoomMaps, ActType, ActSceneId
get_room(?ACT_TYPE_KF, ServerId, _Node, CurLine, RoomLines, RoomMaps, ActSceneId) ->
    RoomList = maps:to_list(RoomMaps),
    case check_room_alloced(ServerId, RoomList) of
        {true, Room} -> %% 该服已被分配房间
            {AllocLine, NewCurLine, NewRoomLines} = select_line_from_alloc_room(Room, CurLine, RoomLines, ActSceneId),
            {Room, AllocLine, NewCurLine, NewRoomLines, RoomMaps};
        _ -> %% 活动期间连上来的新服不处理
            false
        % case alloc_room(ServerId, Node, CurRoom, RoomMaps) of
        %     {new_room, AllocRoom, NewCurRoom} -> %% 分配了新房间
        %         NewRoomMaps = maps:put(AllocRoom, [{ServerId, Node}], RoomMaps),
        %         {AllocLine, NewCurLine, NewRoomLines} = alloc_scene_pool(AllocRoom, CurLine, RoomLines),
        %         {{AllocRoom, AllocLine}, {NewCurRoom, NewCurLine}, {NewRoomMaps, NewRoomLines}};
        %     {slected_room, AllocRoom} -> %% 分配已有的空闲房间,不需再开辟新房间
        %         RoomServers = maps:get(AllocRoom, RoomMaps, []),
        %         NewRoomMaps = maps:put(AllocRoom, [{ServerId, Node}|RoomServers], RoomMaps),
        %         {AllocLine, NewCurLine, NewRoomLines} = select_line_from_alloc_room(AllocRoom, CurLine, RoomLines, ActSceneId),
        %         {{AllocRoom, AllocLine}, {CurRoom, NewCurLine}, {NewRoomMaps, NewRoomLines}}
        % end
    end;
%% 本服默认只有一个区域
get_room(?ACT_TYPE_BF, _ServerId, _Node, CurLine, RoomLines, RoomMaps, ActSceneId) ->
    case maps:get(1, RoomMaps, false) of
        {Room = 1, [{_ServerId, _Node}]} ->
            {AllocLine, NewCurLine, NewRoomLines} = select_line_from_alloc_room(Room, CurLine, RoomLines, ActSceneId),
            {Room, AllocLine, NewCurLine, NewRoomLines, RoomMaps};
        _ ->
            Room = 1,
            NewRoomMaps = maps:put(Room, [{_ServerId, _Node}], RoomMaps),
            {AllocLine, NewCurLine, NewRoomLines} = select_line_from_alloc_room(Room, CurLine, RoomLines, ActSceneId),
            {Room, AllocLine, NewCurLine, NewRoomLines, NewRoomMaps}
    end.

%% 从给定好房间内寻找合适线路
select_line_from_alloc_room(Room, CurLine, RoomLines, ActSceneId) ->
    case maps:get(Room, RoomLines, false) of
        ScenePools = [_|_] -> %% 该房间已有线
            case get_avaliable_scene_pool(Room, ScenePools, CurLine, RoomLines, ActSceneId) of
                {selected_line, AllocLine, NewRoomLines} -> %% 选取一条空闲人数的已有线
                    {AllocLine, CurLine, NewRoomLines};
                {new_line, AllocLine, NewCurLine, NewRoomLines} -> %% 新线
                    {AllocLine, NewCurLine, NewRoomLines}
            end;
        _ -> %% 有房间但还没开线
            alloc_scene_pool(Room, CurLine, RoomLines)
    end.

%% 在房间已有线路中随机取一条
%% 返回{分配好的线,最新可开辟的线,房间=>线路映射}
get_avaliable_scene_pool(Room, ScenePools, CurLine, RoomLines, _ActSceneId) ->
    RoomMaxPeople = data_charm_beach:get_cfg(max_people),
    case [{ScenePoolX, RoleCountX} || {ScenePoolX, RoleCountX} <- ScenePools, RoomMaxPeople > RoleCountX] of
        RestScenePools = [_|_] -> %% 有空余人数的线
            SortPools = lists:keysort(2, RestScenePools),
            [{ScenePool, _RoleCount}|_] = lists:reverse(SortPools),
            NewScenePools = lists:keyreplace(ScenePool, 1, ScenePools, {ScenePool, _RoleCount+1}),
            NewRoomLines = maps:put(Room, NewScenePools, RoomLines), %?ERR("~p", [NewRoomLines]),
            {selected_line, ScenePool, NewRoomLines};
        _ -> %% 线都满了开新线并绑定房间
            {AllocLine, NewCurLine, NewRoomLines} = alloc_scene_pool(Room, CurLine, RoomLines),
            {new_line, AllocLine, NewCurLine, NewRoomLines}
    end.

%% 在给定房间中开辟新线路
%% #{room => [{线路,线路人数}...]}
alloc_scene_pool(Room, CurLine, RoomLines) ->
    case maps:get(Room, RoomLines, false) of
        OneRoomLine = [_|_] ->
            NewRoomLines = maps:put(Room, [{CurLine, 1}|OneRoomLine], RoomLines),
            {CurLine, CurLine+1, NewRoomLines};
        _ ->
            NewRoomLines = maps:put(Room, [{CurLine, 1}], RoomLines),
            {CurLine, CurLine+1, NewRoomLines}
    end.

%% 查看此服是否已被分配房间(区域)
check_room_alloced(_ServerId, []) -> false;
check_room_alloced(ServerId, [{Room, ServerIds}|Rest]) ->
    case lists:member(ServerId, ServerIds) of
        true ->
            {true, Room};
        _ ->
            check_room_alloced(ServerId, Rest)
    end.

%% 给首次进来的服分配房间
alloc_room(_ServerId, _Node, CurRoom, RoomMaps) ->
    case check_rest_room(RoomMaps) of
        false ->
            {new_room, CurRoom, CurRoom+1};
        {true, AllocRoom} ->
            {slected_room, AllocRoom}
    end.

%% 检测空闲的房间
check_rest_room(RoomMaps) ->
    RoomList = maps:to_list(RoomMaps),
    SerNum = data_charm_beach:get_cfg(servers_each_room),
    case do_check_rest_room(RoomList, SerNum) of
        {true, SelectRoom} ->
            {true, SelectRoom};
        _ ->
            false
    end.

do_check_rest_room([], _) -> false;
do_check_rest_room([{Room, ServerIds}|Rest], SerNum) ->
    Len = length(ServerIds),
    if Len < SerNum -> %% 房间还有容量
        {true, Room};
        true ->
            do_check_rest_room(Rest, SerNum)
    end.

%% 获取玩家所在房间当前pool_id
get_current_line(ServerId, RoomMaps, CurLineM) ->
    RoomList = maps:to_list(RoomMaps),
    case check_room_alloced(ServerId, RoomList) of
        {true, Room} -> skip;
        _ ->  Room = 1
    end,
    maps:get(Room, CurLineM, ?ROOM_POOL(Room)).

decre_room_line_role_count(Room, ScenePool, RoomLines) ->
    case maps:get(Room, RoomLines, false) of
        ScenePools = [_|_] ->
            case lists:keyfind(ScenePool, 1, ScenePools) of
                {ScenePool, Count} ->
                    NewScenePools = lists:keyreplace(ScenePool, 1, ScenePools, {ScenePool, max(Count-1, 0)}),
                    maps:put(Room, NewScenePools, RoomLines);
                _ ->
                    RoomLines
            end;
        _ ->
            ?ERR("error when quit scene ~n ~p", [{Room, RoomLines}]),
            RoomLines
    end.

%% 分区,由跨服中心管理分房
calc_zone(SrvZoneMap) ->
    %SortedNodes = lists:keysort(#game_node.open_time, NodeList),
    % SortedList = [{ServerId, Node} || #game_node{server_id = ServerId, node = Node} <- SortedNodes],
    % ReverseList  =lists:reverse(SortedList),
    % ConfigSrvNum = data_charm_beach:get_cfg(servers_each_room),
    ServerIds = maps:keys(SrvZoneMap),
    F = fun(ServerId, TMap) ->
        ZoneId = maps:get(ServerId, SrvZoneMap, 0),
        case maps:get(ZoneId, TMap, []) of
            [_|_] = SrvList ->
                maps:put(ZoneId, [ServerId|SrvList], TMap);
            _ ->
                maps:put(ZoneId, [ServerId], TMap)
        end end,
    ZoneSrvMap = lists:foldl(F, #{}, ServerIds),
    ZoneSrvMap.

%%calc_zone(#{}, RoomMaps,  _) -> RoomMaps;
%%calc_zone(List, RoomMaps, Index) ->
%%    if Len >= ConfigSrvNum ->
%%        case ulists:split(ConfigSrvNum, List) of
%%            {ZoneList, Rest = []} ->
%%                NewRoomMaps = maps:put(Index, ZoneList, RoomMaps),
%%                calc_zone(Rest, NewRoomMaps, ConfigSrvNum, Index+1);
%%            {ZoneList, RestList} ->
%%                NewRoomMaps = maps:put(Index, ZoneList, RoomMaps),
%%                calc_zone(RestList, NewRoomMaps, ConfigSrvNum, Index+1)
%%        end;
%%        true ->
%%            NewRoomMaps = maps:put(Index, List, RoomMaps),
%%            calc_zone([], NewRoomMaps, ConfigSrvNum, Index+1)
%%    end.


gm_next() ->
    ActType = get_act_cls_type(),
    if
        ActType =:= ?ACT_TYPE_KF ->
            mod_charm_beach:gm_start_hosting(),
            clusters_node:apply_cast(mod_charm_beach, gm_next, []);
        true ->
            mod_charm_beach:gm_start_hosting(),
            mod_charm_beach:gm_next()
    end.

gm_start() ->
    ActType = get_act_cls_type(),
    if
        ActType =:= ?ACT_TYPE_KF ->
            clusters_node:apply_cast(mod_charm_beach, gm_start, []);
        true ->
            mod_charm_beach:gm_start()
    end.

gm_stop() ->
    ActType = get_act_cls_type(),
    if
        ActType =:= ?ACT_TYPE_KF ->
            mod_charm_beach:gm_stop_hosting(),
            clusters_node:apply_cast(mod_charm_beach, gm_stop, []);
        true ->
            mod_charm_beach:gm_stop_hosting(),
            mod_charm_beach:gm_stop()
    end.

send_end_tv(Range, Mod, Id, Args) ->
    {scene, SceneId, PoolIds} = Range,
    case PoolIds of
        [_|_] ->
            [lib_chat:send_TV({scene, SceneId, PoolId}, Mod, Id, Args) || PoolId <- PoolIds];
        _ ->
            skip
    end.

%% 转生回调
handle_event(PS, #event_callback{type_id = ?EVENT_TURN_UP}) when is_record(PS, player_status) ->
    #player_status{scene = SceneId, figure = Figure, id = RoleId} = PS,
    ActType = get_act_cls_type(),
    case is_in_scene(SceneId, ActType) of
        true ->
            Node = clusters_node:get_node(),
            if
                ActType =:= ?ACT_TYPE_KF ->
                    clusters_node:apply_cast(mod_charm_beach, player_change_attr, [Node, RoleId, [{figure, Figure}]]);
                true ->
                    mod_charm_beach:player_change_attr(Node, RoleId, [{figure, Figure}])
            end;
        _ ->
            skip
    end,
    {ok, PS};
%% 升级回调
handle_event(PS, #event_callback{type_id = ?EVENT_LV_UP}) when is_record(PS, player_status) ->
    #player_status{scene = SceneId, figure = Figure, id = RoleId} = PS,
    ActType = get_act_cls_type(),
    case is_in_scene(SceneId, ActType) of
        true ->
            Node = clusters_node:get_node(),
            if
                ActType =:= ?ACT_TYPE_KF ->
                    clusters_node:apply_cast(mod_charm_beach, player_change_attr, [Node, RoleId, [{figure, Figure}]]);
                true ->
                    mod_charm_beach:player_change_attr(Node, RoleId, [{figure, Figure}])
            end;
        _ ->
            skip
    end,
    {ok, PS};
%% 战力回调 %% TODO

handle_event(PS, _) ->
    {ok, PS}.

%% 获取拉票信息id
get_vote_id() ->
    case get(vote_id) of
        VoteId when is_integer(VoteId) ->
            put(vote_id, VoteId + 1),
            VoteId + 1;
        _ ->
            put(vote_id, 1),
            1
     end.

calc_price(OldNum, NewNum, _PriceList, Cost) when OldNum > NewNum -> {NewNum, Cost};
calc_price(OldNum, NewNum, PriceList, Cost) ->
    case lists:keyfind(OldNum, 1, PriceList) of
        {OldNum, Price} -> calc_price(OldNum + 1, NewNum, PriceList, Cost + Price);
        _ -> {OldNum, Cost}
    end.

%% --------- 合法性检查 ----------
check(_State, []) ->
    true;
check(State, [Check|CheckList]) ->
    case do_check(State, Check) of
        true ->
            check(State, CheckList);
        {false, ErrCode} ->
            {false, ErrCode}
   end.
%% 玩家是否在活动中
do_check(State, {'in_act', RoleId}) ->
    Members = State#beach_room_stage.members,
    case maps:get(RoleId, Members, false) of
        #beach_member{scene = Scene} when Scene =/= 0 ->
            true;
        _ -> {false, ?ERRCODE(err418_not_join_act)}
    end;
%% 被投票者是否在活动中
do_check(State, {'opp_in_act', BeVoteId}) ->
    Members = State#beach_room_stage.members,
    case maps:get(BeVoteId, Members, false) of
        #beach_member{} ->
            true;
        _ -> {false, ?ERRCODE(err418_opp_not_in_act)}
    end;
%% 是否有足够的投票次数
do_check(State, {'enough_times', RoleId, AddFreeTimes}) ->
    Members = State#beach_room_stage.members,
    #beach_member{vote_times = VoteTimes, buy_times = BuyTimes} = maps:get(RoleId, Members),
    FreeTimes   = data_charm_beach:get_cfg(free_vote_times),
    if
        VoteTimes < FreeTimes + AddFreeTimes + BuyTimes -> true;
        true -> {false, ?ERRCODE(err418_not_vote_times)}
    end;
%% 不能投票给自己
do_check(_State, {'self_vote', RoleId, VoteId}) ->
    case RoleId == VoteId of
        true ->
            {false, ?ERRCODE(err418_self_vote)};
        _ ->true
    end;
%% 容错
do_check(_State, CheckMsg) ->
   ?ERR("bad match do_check:~p~n",[CheckMsg]),
   {false, ?FAIL}.
