%%%-----------------------------------
%%% @Module  : lib_kf_1vn
%%% @Email   : ming_up@foxmail.com
%%% @Created : 2018.1.24
%%% @Description: 跨服1vN 通用库函数
%%%-----------------------------------
-module(lib_kf_1vn).

-include("common.hrl").
-include("server.hrl").
-include("scene.hrl").
-include("predefine.hrl").
-include("figure.hrl").
-include("career.hrl").
-include("kf_1vn.hrl").
-include("attr.hrl").
-include("errcode.hrl").
-include("def_event.hrl").
-include("def_module.hrl").
-include("rec_event.hrl").
-include("def_fun.hrl").
-include("shop.hrl").
-include("def_act_label.hrl").

-export([
        login/1,
        player_die/1,
        trans_enter_info/1,
        logout/1,
        reconnect/2,
        check_def_shop/2,
        check_def_shop/1,
        check_challenger_shop/1,
        check_area/2,
        update_player_1vn/4,
        update_player_1vn/5,
        update_race_2_rank/3,
        set_player_1vn/4,
        clean_1vn/0,
        db_save_sign/1,
        db_clean_sign/0,
        get_sign_list/0,
        handle_event/2,
        auction_lose/3,
        return_auction_cost/2,
        auction_success/4,
        finish_into_pk_scene/2
    ]).
-export([
        dummy_die/5,
        trans_to_pk_role/2,
        trans_to_pk_role/1,
        trans_to_pk_robot/1,
        send_all_enter/2,
        pre_scene_xy/0,
        create_robot_pk/6,
        quit_to_main_scene/2,
        db_get_def_rank/0,
        db_save_def_rank/2,
        clean_def_rank/0,
        get_sign_list_center/0,
        db_save_sign_center/6,
        db_clean_sign_center/0,
        pull_into_pk_scene/7,
        make_winner_list/1,
        send_to_area/2,
        get_area_name/1,
        send_to_challenger/2,
        test_race2_match/3,
        get_goods_list/2,
        is_skip_this_match/2,
        is_in_1vN_pre/1,
        update_player_act_label/3,
        update_player_act_label/2,
        send_mail/5,
        get_real_hard_list/5
    ]).


login(Id) ->
    case db:get_row(io_lib:format(<<"select def_type, turn, area from player_1vn where id=~w limit 1">>, [Id])) of
        [DefType, Turn, Area] -> #status_kf1vn{def_type=DefType, turn=Turn, area = Area};
        _ -> #status_kf1vn{}
    end.

%% 进入活动获取玩家身上所需要的信息
trans_enter_info(PS) ->
    #player_status{id=Id, server_id=ServerId, server_num=ServerNum, figure=Figure, battle_attr=#battle_attr{attr=Attr}, combat_power = CombatPower, server_name = ServerName} = PS,
    #kf_1vn_role{id=Id, server_id=ServerId, server_num=ServerNum, figure=Figure, attr=Attr, combat_power=CombatPower, server_name = ServerName}.

%% 玩家死亡回调
player_die(Status) ->
    case is_pid(Status#player_status.copy_id) andalso is_in_1vN_pk(Status#player_status.scene) of
        true -> clusters_node:apply_cast(erlang, send, [Status#player_status.copy_id, {player_die, Status#player_status.id}]);
        false -> skip
    end.

%% 是否在pk场景中
is_in_1vN_pk(SceneId) ->
    PkScenes = data_kf_1vn_m:get_config(pk_scenes),
    lists:member(SceneId, PkScenes).

%% 是否在备战场景中
is_in_1vN_pre(SceneId) ->
    PreScenes = data_kf_1vn_m:get_config(pre_scene),
    lists:member(SceneId, PreScenes).

%% 是否在跨服1vn场景中
is_in_1vN(SceneId) ->
    PkScenes = data_kf_1vn_m:get_config(scenes),
    lists:member(SceneId, PkScenes).

%% 活动中登出
logout(PS) ->
    case is_in_1vN(PS#player_status.scene) of
        true  -> mod_kf_1vn_local:quit(PS#player_status.id, PS#player_status.copy_id, 0);
        false -> skip
    end.

%% 重连
reconnect(PS, _) ->
    case is_in_1vN(PS#player_status.scene) of
        true  ->
            mod_kf_1vn_local:reconnect(PS#player_status.id),
            {next, PS};
        false ->
            {next, PS}
    end.

%% 检测擂主商店是否可购买
check_def_shop(Status, BuyTurn) ->
    #player_status{kf_1vn=#status_kf1vn{def_type=DefType, turn=Turn}} = Status,
    if
        DefType /= 1 -> false;
        BuyTurn > Turn -> false;
        true -> true
    end.
check_def_shop(Status) ->
    #player_status{kf_1vn=#status_kf1vn{def_type=DefType}} = Status,
    if
        DefType == 1 -> true;
        true -> false
    end.

%% 检测挑战者商城
check_challenger_shop(Status) ->
    #player_status{kf_1vn=#status_kf1vn{def_type=DefType}} = Status,
    if
        DefType == 2 -> true;
        true -> false
    end.

%% 赛区检测
check_area(Status, NeedArea) ->
    #player_status{kf_1vn=#status_kf1vn{area=Area}} = Status,
    if
        NeedArea == Area -> true;
        true -> false
    end.

update_player_1vn(SerId, RoleId, DefType, Turn, Area) ->
    clusters_center:apply_cast(SerId, lib_kf_1vn, update_player_1vn, [RoleId, DefType, Turn, Area]).
%% 更新玩家1vn的数据
update_player_1vn(RoleId, DefType, Turn, Area) ->
    db:execute(io_lib:format(<<"replace into player_1vn set id=~w, def_type=~w, turn=~w, area=~w">>, [RoleId, DefType, Turn, Area])),
    lib_player:update_player_info(RoleId, [{kf_1vn, {DefType, Turn, Area}}]),
    ok.

%% 设置玩家1vn数据
set_player_1vn(Status, DefType, Turn, Area) ->
    Status#player_status{kf_1vn=#status_kf1vn{def_type=DefType, turn=Turn, area = Area}}.

%% 清理1vn数据库
clean_1vn() ->
    db:execute("truncate player_1vn"),
    ok.

%% 从数据库获取报名列表
get_sign_list() ->
    L = db:get_all(<<"select id from kf_1vn_sign ">>),
    [{Id, 0} || [Id] <- L].

%% 保存报名的玩家
db_save_sign(Id) ->
    db:execute(io_lib:format(<<"replace into kf_1vn_sign set id=~w, time=~w">>, [Id, utime:unixtime()])).

%% 清空报名数据
db_clean_sign() ->
    db:execute(<<"truncate kf_1vn_sign">>).


%% ==================== 跨服中心调用 =====================================================

%% 假人死亡 (Object1, [], Atter, AtterSign, Args)
dummy_die(#scene_object{scene = SceneId, id = Id, copy_id = CopyId}, _, _Atter, _AtterSign, _Args) ->
    case is_pid(CopyId) andalso is_in_1vN_pk(SceneId) of
        true -> CopyId ! {dummy_die, Id};
        false -> skip
    end.

%% 发送给参赛者
send_all_enter([#kf_1vn_role{server_id=SerId, id=Id, enter=Enter}|T], BinData) when Enter =/= 0 ->
    clusters_center:apply_cast(SerId, lib_server_send, send_to_uid, [Id, BinData]),
    send_all_enter(T, BinData);
send_all_enter([_|T], BinData) ->  send_all_enter(T, BinData);
send_all_enter(_, _) -> skip.

%% 创建机器人（守擂赛）
create_robot_pk(Hard, _Attr, Area, RobotId, ServerNum, MatchTurn) ->
    %% 随机形象
    Career = urand:list_rand(?ALL_CAREER_LIST),
    case data_career:get(Career) of
        #career{sex = Sex} ->skip;
        _ -> Sex = ?MALE
    end,
    RobotSerNum = urand:rand(1, ServerNum),
    Name = get_name(Sex),
    Turn = 3,
    %% 根据等级和战力创建假人
    {FakeRoleLv, RobotAttr, RobotCombatPower} = get_faker_role_attr(?KF_1VN_TYPE_RACE_2, Area, Hard),
    WinTime = urand:rand(1, MatchTurn - 1),
    #kf_1vn_role_pk{type=?KF_1VN_C_TYPE_ROBOT, id=RobotId, server_num=RobotSerNum, area=Area, name=Name, hard=Hard, career=Career, attr=RobotAttr, combat_power=RobotCombatPower, sex = Sex, turn = Turn, lv = FakeRoleLv, win = WinTime}.

%% 创建机器人（资格赛）
trans_to_pk_robot(#kf_1vn_role{id=Id, server_num=ServerNum,combat_power=CombatPower, area = Area, win_streak=WinStreak}) ->
    %% 随机形象
    RobotId = Id band 16#FFFFFFFF,
    Career = urand:list_rand(?ALL_CAREER_LIST),
    case data_career:get(Career) of
        #career{sex = Sex} -> skip;
        _ -> Sex = ?MALE
    end,
    Name = get_name(Sex),
    Turn = 3,
    %% 根据难度获取假人等级和属性系数
    case data_kf_1vn:get_hard(CombatPower) of
        0 -> Hard = data_kf_1vn:get_max_hard();
        Hard -> skip
    end,
    {FakeRoleLv, RobotAttr, RobotCombatPower} = get_faker_role_attr(?KF_1VN_TYPE_RACE_1, Area, Hard),
    #kf_1vn_role_pk{type=?KF_1VN_C_TYPE_ROBOT, id=RobotId, server_num=ServerNum, area=Area, name=Name, hard=1, career=Career,
                    attr=RobotAttr, combat_power=RobotCombatPower, score=0, win=0, win_streak=WinStreak, race_1_times=1,
                    sex = Sex, turn = Turn, lv = FakeRoleLv}.

get_name(Sex) ->
    Group = data_vsn:calc_name_group(),
    FirstName = urand:list_rand(data_role_name:first_name_list(Group, Sex)),
    LastName  = urand:list_rand(data_role_name:last_name_list(Group)),
    util:make_sure_binary(LastName ++ FirstName).

get_faker_role_attr(Type, Area, Hard) ->
    case data_kf_1vn:get_fake_role_cfg(Type, Area, Hard) of
        {FakeRoleLv, {MinCombat, MaxCombat}, Ratio} ->
            RobotCombatPower = urand:rand(MinCombat, MaxCombat),
            #battle_attr{attr = RobotAttr} = lib_fake_role:get_faker_role_attr(FakeRoleLv, RobotCombatPower, (Ratio / 100));
        _ ->
            FakeRoleLv = 700,
            #battle_attr{attr = RobotAttr, combat_power = RobotCombatPower} = lib_fake_role:get_faker_role_attr(FakeRoleLv)
    end,
    {FakeRoleLv, RobotAttr, RobotCombatPower}.

%% 角色信息幻化为pk信息
trans_to_pk_role(#kf_1vn_role_pk{type=?KF_1VN_C_TYPE_ROBOT}=H, _Area) ->
    H;
trans_to_pk_role(#kf_1vn_role{id=Id, server_id=ServerId, server_num=ServerNum, score=Score, figure=#figure{name=Name, career=Career, sex = Sex, turn = Turn, lv = Lv, picture = Picture, picture_ver = PictureVer}, win=Win, win_streak=WinStreak, race_1_times=Race1Times, combat_power=CombatPower, server_name = ServerName, scene_pool_id = PoolId}, Area) ->
    #kf_1vn_role_pk{type=?KF_1VN_C_TYPE_PLAYER, id=Id, server_id=ServerId, server_num=ServerNum, career=Career, name=Name, area=Area, score=Score, win=Win, win_streak=WinStreak, race_1_times=Race1Times, combat_power=CombatPower, server_name = ServerName, sex = Sex, turn = Turn, scene_pool_id = PoolId, lv = Lv, picture = Picture, picture_ver = PictureVer}.

trans_to_pk_role(#kf_1vn_role{area = Area} = Player) ->
trans_to_pk_role(Player, Area);
trans_to_pk_role(Player) when is_record(Player, kf_1vn_role_pk) -> Player.

%% 获取备战场景的出生点
pre_scene_xy() ->
    case data_kf_1vn:get_value(?KF_1VN_CFG_PER_SCENE_XY) of
        false -> {0, 0};
        List ->
            Length = length(List),
            Num = case get(?PRE_SCENE_XY_KEY) of
                TmpNum when is_integer(TmpNum) -> max(min(TmpNum, Length), 1);
                _ -> urand:rand(1, Length)
            end,
            {X, Y, R} = lists:nth(Num, List),
            NewNum = ?IF(Num >= Length, 1, Num + 1),
            put(?PRE_SCENE_XY_KEY, NewNum),
            umath:rand_point_in_circle(X, Y, R, 1)
    end.

%% 退出活动场景
quit_to_main_scene(SerIdOrNode, Id) ->
    clusters_center:apply_cast(SerIdOrNode, lib_scene, change_player_scene, [Id, 0, 0, 0, [{group, 0}, {ghost, 0}, {action_free, ?ERRCODE(err621_in_kf_1vn)}, {act_label, [{?ACT_LABEL_KF_1VN_SIDE, 0}]}]]).

%% 竞拍失败，返还预扣款
auction_lose(RoleId, GoodsStr, Refs) ->
    Callback = {?MODULE, return_auction_cost, [RoleId, GoodsStr]},
    [lib_consume_data:advance_payment_fail(RoleId, Ref, Callback) || Ref <- Refs].

%% 返还预扣款
return_auction_cost(CostList, [RoleId, GoodsStr]) ->
    Title = utext:get(6210019),
    Content = utext:get(6210020, [GoodsStr]),
    lib_mail_api:send_sys_mail([RoleId], ?MOD_KF_1VN, Title, Content, CostList).

%% 竞拍成功，扣除预扣款并且发送奖励
auction_success(RoleId, Goods, Refs, Price) ->
    [lib_consume_data:advance_payment_done(RoleId, Ref) || Ref <- Refs],
    Title = utext:get(6210011),
    GoodsStr = util:make_goods_str(Goods),
    Content = utext:get(6210012, [Price, GoodsStr]),
    lib_mail_api:send_sys_mail([RoleId], ?MOD_KF_1VN, Title, Content, Goods).

get_sign_list_center() ->
    L = db:get_all(<<"select id, server_id, lv, combat_power, `name`, `server_name` from kf_1vn_sign_center ">>),
    F = fun([Id, ServerId, Lv, CombatPower, Name, ServerName]) ->
        #kf_1vn_role_sign{id=Id, server_id=ServerId, lv=Lv, combat_power=CombatPower, name = Name, server_name = ServerName}
    end,
    lists:map(F, L).

db_save_sign_center(ServerId, Id, Lv, CombatPower, Name, ServerName) ->
    db:execute(io_lib:format(<<"replace into kf_1vn_sign_center set id=~w, server_id=~w, lv=~w, combat_power=~w, `name`='~ts', `server_name`='~ts', time=~w">>, [Id, ServerId, Lv, CombatPower, Name, ServerName, utime:unixtime()])).

db_clean_sign_center() ->
    db:execute(<<"truncate kf_1vn_sign_center">>).

db_get_def_rank() ->
    L = db:get_all(<<"select area, rank, server_id, server_num, server_name, player_id, name, gname, vip, score, turn, combat_power, career, survival_time, lose from kf_1vn_def_rank">>),
    F = fun([Area, Rank, SerId, SerNum, SerName, Id, Name, GuildName, Vip, Score, Turn, CP, Career, SurvivalTime, Lose], TmpRace2M) ->
            #kf_1vn_race_2{def_rank=DefRankList} = Race2 = maps:get(Area, TmpRace2M, #kf_1vn_race_2{}),
            DefRank = #kf_1vn_def_rank{rank=Rank,server_id=SerId,id=Id,server_num=SerNum,name=Name,guild_name=GuildName,vip=Vip,score=Score,race_2_turn=Turn,combat_power=CP,career=Career,race_2_time=SurvivalTime,race_2_lose=Lose,server_name=SerName},
            NewDefRankList = [DefRank | DefRankList],
            TmpRace2M#{Area=>Race2#kf_1vn_race_2{def_rank= NewDefRankList}}
    end,
    Map = lists:foldl(F, #{}, L),
    maps:map(fun(_Area, #kf_1vn_race_2{def_rank = DefRank} = Race2) ->
        Race2#kf_1vn_race_2{def_rank = mod_kf_1vn:sort_def_rank(DefRank)}
        end, Map).

db_save_def_rank([], _Area) -> ok;
db_save_def_rank(DefRank, Area) ->
    ValueSql = db_save_def_rank_sql(DefRank, Area, "", 1),
    Sql = "replace into kf_1vn_def_rank (area, rank, server_id, server_num, server_name, player_id, name, gname, vip, score, turn, combat_power, career, survival_time, lose) values " ++ ValueSql,
    db:execute(Sql),
    ok.

db_save_def_rank_sql([], _Area, TmpSql, _Index) -> TmpSql;
db_save_def_rank_sql([#kf_1vn_def_rank{rank=Rank, server_id=SerId, id=Id, server_num=SerNum, name=Name, guild_name=GuildName, vip=Vip, score=Score, race_2_turn=Turn, combat_power=CP, career=Career, race_2_time=Race2Time, race_2_lose=Race2Lose, server_name = SerName}|T], Area, TmpSql, Index) ->
    TmpSql1 = case Index of
        1 -> io_lib:format("(~w, ~w, ~w, ~w, '~s', ~w, '~s', '~s', ~w, ~w, ~w, ~w, ~w, ~w, ~w) ", [Area, Rank, SerId, SerNum, SerName, Id, Name, GuildName, Vip, Score, Turn, CP, Career, Race2Time, Race2Lose]) ++ TmpSql;
        _ -> io_lib:format("(~w, ~w, ~w, ~w, '~s', ~w, '~s', '~s', ~w, ~w, ~w, ~w, ~w, ~w, ~w), ", [Area, Rank, SerId, SerNum, SerName, Id, Name, GuildName, Vip, Score, Turn, CP, Career, Race2Time, Race2Lose]) ++ TmpSql
    end,
    db_save_def_rank_sql(T, Area, TmpSql1, Index+1).

clean_def_rank() ->
    db:execute(<<"truncate kf_1vn_def_rank">>).


%% 玩家战力改变时更新报名数据
handle_event(Player, #event_callback{type_id = ?EVENT_COMBAT_POWER}) ->
    case Player of
        #player_status{combat_power = Power, id = Id} ->
            mod_kf_1vn_local:update_sign_power(Id, Power);
        _ ->
            ok
    end,
    {ok, Player}.

%% 传送入pk场景
pull_into_pk_scene(RoleId, SceneId, ScenePoolId, CopyId, X, Y, Args) ->
    lib_player_event:async_add_listener(RoleId, ?EVENT_FIN_CHANGE_SCENE, ?MODULE, finish_into_pk_scene, [SceneId, ScenePoolId, CopyId]),
    lib_scene:change_player_scene(RoleId, SceneId, ScenePoolId, CopyId, X, Y, [{act_label, [{?ACT_LABEL_KF_1VN_SIDE, 0}]}|Args]).

%% 传送场景结束监听
finish_into_pk_scene(Player, #event_callback{param = [SceneId, ScenePoolId, CopyId]}) ->
    case Player of
        #player_status{scene = SceneId, scene_pool_id = ScenePoolId, copy_id = CopyId} ->
            lib_player_event:remove_listener(?EVENT_FIN_CHANGE_SCENE, ?MODULE, finish_into_pk_scene),
            CopyId ! {role_enter_finish, Player#player_status.id};
        _ ->
            ok
    end,
    {ok, Player}.

make_winner_list(DefRank) ->
    WinnerList = [ Name || #kf_1vn_def_rank{server_num = _ServerNum, name = Name, race_2_lose = Race2Lose} <- DefRank, Race2Lose == 0],
    util:link_list_extra(WinnerList).

send_to_area(Id, BinData) ->
    mod_kf_1vn_local:send_to_area(Id, BinData).

get_area_name(Area) ->
    case Area of
        1 -> utext:get(6210013);
        _ -> utext:get(6210014)
    end.

%% 通知被选为挑战者
send_to_challenger(Area, MatchL) ->
    {ok, BinData} = pt_621:write(62134, [1]),
    F2 = fun(#kf_1vn_role_pk{id=Id, server_id=SerId, type=Type}) ->
        case Type of
            ?KF_1VN_C_TYPE_PLAYER ->
                mod_kf_1vn:update_role(Area, Id, [{match_challenger, 1}]),
                clusters_center:apply_cast(SerId, lib_server_send, send_to_uid, [Id, BinData]);
            _ -> skip
        end
    end,
    F1 = fun(#kf_1vn_race_2_match_info{challengers = Challengers}) ->
        lists:foreach(F2, Challengers)
    end,
    lists:foreach(F1, MatchL).

test_race2_match(Turn, Area, Combat) ->
    case data_kf_1vn:get_stage_args(Turn, Area) of
        #kf_1vn_race_2_match{hard_list = Hard, c_num = ChallengerNum, all_hard = AllHard} -> %when MatchTurn < 2 ->
            [NewHard, NewAllHard] = do_get_real_hard_list(Area, Combat, Hard, AllHard),
            _HardList = mod_kf_1vn:create_race_2_hard_list(NewHard, ChallengerNum, NewAllHard),
            ?DEBUG("Turn:~p~nHardList:~p~n", [Turn, util:term_to_string(_HardList)]),
            ok;
        _ -> skip
    end.

%% 获取商城内的商品
get_goods_list(Player, ShopType) ->
    #player_status{id = RoleId, sid = Sid, figure = #figure{lv = Lv}, kf_1vn=#status_kf1vn{area = Area}} = Player,
    if %% 玩家的赛区为参与比赛的赛区
        Area == 1 orelse Area == 2 ->
            NewArea = Area;
        true -> %% 未参与比赛的赛区直接根据等级读取
            NewArea = data_kf_1vn:get_area(Lv)
    end,
    ServerTime = util:get_open_day(),
    WorldLv = util:get_world_lv(),
    KeyList = data_shop:get_shop(ShopType),
    F = fun(KeyId, GoodsList) ->
        case data_shop:get_shop_config(KeyId) of
            #shop{key_id = KeyId, shop_type = ShopType, rank = Rank, ctype = MoneyType,
                  goods_id = GoodsId, num = Num, price = Price, discount = Discount,
                  quota_type = QuotaType, quota_num = TmpQuotaNum, on_sale = OnSale,
                  halt_sale = HaltSale,counter_module = Module, counter_id = CounterId,
                  bind = Bind,wlv_sale = WlvSale, wlv_unsale = WlvUnsale, condition = Condition, preview = Preview} ->
                  if
                    (OnSale > ServerTime) orelse (ServerTime > HaltSale andalso HaltSale /= 0) -> GoodsList;
                    (WlvSale > WorldLv) orelse (WorldLv > WlvUnsale andalso WlvUnsale /= 0) -> GoodsList;
                    true ->
                        case lists:keyfind(kf_1vn_area, 1, Condition) of
                            {kf_1vn_area, NeedArea} when NeedArea =/= NewArea ->
                                GoodsList;
                            _ ->
                                case util:term_to_string(Condition) of
                                    undefined ->
                                        ?ERR("bad condition ~p~n", [Condition]),
                                        GoodsList;
                                    StrCondition ->
                                        %% 购买次数容错一下，避免策划忘记配置计数器id
                                        QuotaNum = lib_shop:get_quota_num(Player, Condition, TmpQuotaNum),
                                        UsedTime = ?IF(QuotaNum > 0, min(lib_shop_check:get_buyed_time(RoleId, KeyId, Module, CounterId, QuotaType), QuotaNum), 0),
                                        [{KeyId, Rank, GoodsId, Num, MoneyType, Price, Discount, QuotaType, QuotaNum, UsedTime, StrCondition, Bind, Preview}|GoodsList]
                                end
                        end
                  end;
            _ -> GoodsList
        end
    end,
    NewGoodsList = lists:foldl(F, [], KeyList),
    {ok, BinData} = pt_153:write(15301, [ShopType, NewGoodsList]),
    lib_server_send:send_to_sid(Sid, BinData).

%% 资格赛是否跳过本次匹配
is_skip_this_match(PkTime, MatchTime) ->
    Race1CanMatchTime = data_kf_1vn:get_value(?KF_1VN_CFG_ENTER_MATCH_TIME),
    MatchTime - PkTime < Race1CanMatchTime.

%% 改变玩家的act_label
update_player_act_label(SerId, Id, Args) ->
    clusters_center:apply_cast(SerId, lib_kf_1vn, update_player_act_label, [Id, Args]).
update_player_act_label(Id, Args) when is_integer(Id) ->
    lib_player:apply_cast(Id, ?APPLY_CAST_STATUS, ?MODULE, update_player_act_label, [Args]);
update_player_act_label(PS, Args) when is_record(PS, player_status) ->
    mod_scene_agent:update(PS, [{act_label, Args}]),
    lib_server_send:send_to_sid(PS#player_status.sid, pt_120, 12089, [PS#player_status.id, Args]),
    lib_scene:broadcast_player_act_label(PS, Args).

%% 同步排行榜到本地节点
update_race_2_rank(Area, ShortDefRank, ShortChallenageRank) ->
    clusters_center:apply_to_all_node(mod_kf_1vn_local, update_race_2_rank, [Area, ShortDefRank, ShortChallenageRank], 50).

send_mail(SerId, IdList, Title, Content, GoodsList) ->
    clusters_center:apply_cast(SerId, lib_mail_api, send_sys_mail, [IdList, ?MOD_KF_1VN, Title, Content, GoodsList]).

get_real_hard_list(Area, DefList, Roles, [MinHard, MaxHard], [MinAllHard, MaxAllHard]) ->
    MaxDefCombat = lists:max([get_combat(DefId, Roles) || {DefId, _DefAttr} <- DefList]), %% 擂主最大的战力
    do_get_real_hard_list(Area, MaxDefCombat, [MinHard, MaxHard], [MinAllHard, MaxAllHard]).

do_get_real_hard_list(Area, MaxDefCombat, [MinHard, MaxHard], [MinAllHard, MaxAllHard]) ->
    Stage = data_kf_1vn:get_stage_by_area(Area),
    #kf_1vn_race_2_match{hard_list = [_,TmpMaxHard]} = data_kf_1vn:get_stage_args(Stage, Area),
    CombatCalc = data_kf_1vn_m:get_config(combat_calc),
    Coefficient = MaxDefCombat / TmpMaxHard / CombatCalc,
    NewHard = [trunc(MinHard * Coefficient), trunc(MaxHard * Coefficient)],
    NewAllHard = [trunc(MinAllHard * Coefficient), trunc(MaxAllHard * Coefficient)],
    [NewHard, NewAllHard].

%% 获取战力
get_combat(RoleId, Roles) ->
    case lists:keyfind(RoleId, #kf_1vn_role.id, Roles) of
        #kf_1vn_role{combat_power = CombatPower} -> CombatPower;
        _ -> 0
    end.
