%% ---------------------------------------------------------------------------
%% @doc lib_player
%% @author ming_up@foxmail.com
%% @since  2016-08-03
%% @deprecated  角色库函数
%% ---------------------------------------------------------------------------
-module(lib_player).
-include("common.hrl").
-include("sql_player.hrl").
-include("server.hrl").
-include("attr.hrl").
-include("figure.hrl").
-include("rec_event.hrl").
-include("def_event.hrl").
-include("title.hrl").
-include("designation.hrl").
-include("predefine.hrl").
-include("def_fun.hrl").
-include("battle.hrl").
-include("skill.hrl").
-include("goods.hrl").
-include("relationship.hrl").
-include("errcode.hrl").
-include("scene.hrl").
-include("flower.hrl").
-include("chat.hrl").
-include("dungeon.hrl").
-include("rec_dress_up.hrl").
-include("activitycalen.hrl").
-include("guild.hrl").
-include("team.hrl").
-include("def_module.hrl").
-include("vip.hrl").
-include("fishing.hrl").
-include("limit_goal.hrl").
-include("combat_uplv.hrl").
-include("def_goods.hrl").
-include("role_foster.hrl").
-include("profile_photo.hrl").
-include("degree.hrl").
-include("pet.hrl").
-include("child.hrl").
-include("arena.hrl").
-include("role.hrl").
-include("fashion.hrl").
-include("role_visus.hrl").
-include("def_vip.hrl").
-include("achievement.hrl").
-include("rec_offline.hrl").
-include("fashion_workroom.hrl").
-include("exp_dun_drop.hrl").
-compile(export_all).

%% 导出类型，方便其他模块不需要包含server.hrl时，写spec可以直接用
-export_type([ player/0 ]).
-type(player() :: #player_status{}).

%% ----------------------------------------------------------------
%% 后台接口
%% ----------------------------------------------------------------

%% 封号
limit_login(Ids) when is_list(Ids) ->
    case util:string_to_term(Ids) of
        undefined -> skip;
        L ->
            [limit_login(Id, false) || Id <- L]
            %% 封号不用删除玩家聊天缓存
            %% lib_chat:del_all_msg(L)
    end,
    ok;
limit_login(Id) ->
    limit_login(Id, true),
    ok.

limit_login(Id, NeedDelCache) ->
    RolePid = misc:get_player_process(Id),
    case is_pid(RolePid) andalso misc:is_process_alive(RolePid) of
        true ->
            %% 在玩家进程设置封号标识
            RolePid ! {'limit_login', Id};
        false ->
            lib_server_send:send_to_uid(Id, close)
    end,
    case NeedDelCache of
        true  -> lib_chat:del_all_msg([Id]);
        false -> ignore
    end,
    ok.

%% 更新玩家是否能上传头像
%% @param IdList [Id, Id|...] 玩家列表
%% @param PictureLim 0:允许上传头像,1:禁止上传头像
update_player_picture_lim(IdList, PictureLim) ->
    F = fun(RoleId)->
            %% 检查目标玩家是否在线
            lib_player:update_player_info(RoleId, [{picture_lim, PictureLim}]),
            timer:sleep(100), % 防止过多插入导致其他功能连接不了数据库
            db_update_player_picture_lim(RoleId, PictureLim)
    end,
    lists:foreach(F, IdList),
    1.

%% 更新玩家是否能上传头像
db_update_player_picture_lim(RoleId, PictureLim) ->
    SQL = io_lib:format(?sql_update_picture_lim, [PictureLim, RoleId]),
    db:execute(SQL).

%% 删除玩家上传的头像
delete_player_picture(IdList) ->
    F = fun(RoleId) ->
            case db_get_player_picture_by_role_id(RoleId) of
                [] -> 0;
                [_OldPicture, OldPictureVer] ->
                    Picture = "", PictureVer = OldPictureVer+1,
                    %% 检查目标玩家是否在线
                    case mod_chat_agent:lookup(RoleId) of
                        [] ->
                            db_update_player_picture(RoleId, Picture, PictureVer,0),
                            lib_log_api:log_picture(RoleId, Picture, PictureVer);
                        [_Player] ->
                            lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, update_player_picture_online, [Picture, PictureVer])
                    end,
                    notify_picture_change(RoleId, Picture, PictureVer),
                    {ok, BinData} = pt_130:write(13085, [RoleId, Picture, PictureVer]),
                    lib_server_send:send_to_all(BinData)
            end
    end,
    lists:foreach(F, IdList),
    1.

%% 根据PictureId更新头像信息
db_update_player_picture(RoleId, Picture, PictureVer,ProfilePhotoId) ->
    SQL = io_lib:format(?sql_update_picture_info, [Picture, PictureVer,ProfilePhotoId, RoleId]),
    db:execute(SQL).

db_update_local_player_picture(RoleId,ProfilePhotoId,Picture) ->
    SQL = io_lib:format(?sql_update_local_picture_info,[Picture,ProfilePhotoId,RoleId]),
    db:execute(SQL).

%% 根据PictureId查找玩家id
db_get_player_picture_by_role_id(RoleId) ->
    SQL = io_lib:format(?sql_player_picture_by_role_id, [RoleId]),
    db:get_row(SQL).

%% 注意:在线不在线都需要处理头像的话,通过 notify_picture_change/3 函数处理
%% 更新玩家身上picture
update_player_picture_online(Player, Picture, PictureVer) ->
    #player_status{id = RoleId, figure = Figure} = Player,
    NewFigure = Figure#figure{picture = Picture, picture_ver = PictureVer,profile_photo_id = 0},
    NewPlayer = Player#player_status{figure = NewFigure},
    db_update_player_picture(RoleId, Picture, PictureVer,0),
    lib_log_api:log_picture(RoleId, Picture, PictureVer),
    EventData = #callback_picture{role_id = RoleId, picture = Picture, picture_ver = PictureVer},
    {ok, NewPlayer2} = lib_player_event:dispatch(NewPlayer, ?EVENT_PICTURE_CHANGE, EventData),
    {ok, NewPlayer2}.

%% 更新玩家内置头像
update_player_local_picture_online(Player,ProfilePhotoId) ->
    #player_status{id = RoleId, figure = Figure} = Player,
    NewFigure = Figure#figure{profile_photo_id = ProfilePhotoId,picture = ""},
    NewPlayer = Player#player_status{figure = NewFigure},
    db_update_local_player_picture(RoleId,ProfilePhotoId,""),
    lib_log_api:log_local_picture(RoleId,ProfilePhotoId),
    EventData = #callback_picture{role_id = RoleId, profile_photo_id = ProfilePhotoId},
    {ok, NewPlayer2} = lib_player_event:dispatch(NewPlayer, ?EVENT_PICTURE_CHANGE, EventData),
    {ok, NewPlayer2}.

%% 触发头像更改(在线不在线都需要处理)
notify_picture_change(RoleId, Picture, PictureVer) ->
    EventData = #callback_picture{role_id = RoleId, picture = Picture, picture_ver = PictureVer},
    lib_event:dispatch(?EVENT_PICTURE_CHANGE, EventData),
    mod_guild:update_guild_member_attr(RoleId, [{picture, Picture}, {picture_ver, PictureVer}]),
    % lib_relationship:update_rela_role_info(RoleId, [{picture, Picture}, {picture_ver, PictureVer}]),
    ok.

%% ---------------------------------------------------------------------------
%% @doc 加载角色已激活头像列表
-spec load_role_profile_photo_info(RoleId) -> PhotoInfo when
    RoleId      :: integer(),
    PhotoInfo   :: #profile_photo_info{}.
%% ---------------------------------------------------------------------------
load_role_profile_photo_info(RoleId) ->
    case db:get_all(io_lib:format(?SELECT_PHOTO_LIST, [RoleId])) of
        [[PhotoListBin] | _] ->
            case util:bitstring_to_term(PhotoListBin) of
                PhotoList when is_list(PhotoList) ->
                    PhotoInfo = #profile_photo_info{photo_list = PhotoList},
                    PhotoInfo2 = calc_profile_photo_attr(PhotoInfo),
                    PhotoInfo2;
                _ ->
                    ?ERR("load_role_profile_photo_info error:~ts", [PhotoListBin]),
                    #profile_photo_info{}
            end;
        [] ->
            #profile_photo_info{}
    end.


%% ----------------------------------------------------------------
%% player处理
%% ----------------------------------------------------------------

%% 根据账户名称查找最近创建的角色Id
get_role_last_id(AccId, AccName) ->
    db:get_all(io_lib:format(?SQL_ROLE_LAST_ID, [AccId, AccName])).

%% 获取player_login登陆所需数据
get_player_login_data(Id) ->
    db:get_row(io_lib:format(?sql_player_login_data, [Id])).

%% 获取player_high登陆所需数据
get_player_high_data(Id) ->
    db:get_row(io_lib:format(?sql_player_high_data, [Id])).

%% 获取player_low登陆所需数据
get_player_low_data(Id) ->
    db:get_row(io_lib:format(?sql_player_low_data, [Id])).

%% 获取player_state登陆所需数据
get_player_state_data(Id) ->
    db:get_row(io_lib:format(?sql_player_state_data, [Id])).

%% 获取player_attr登陆所需数据
get_player_attr_data(Id) ->
    db:get_row(io_lib:format(?sql_player_attr_data, [Id])).

%% 根据玩家名字获得对应玩家id
get_role_id_by_name(Name) ->
    db:get_one(io_lib:format(?sql_role_id_by_name, [Name])).

%% 根据玩家id获取对应玩家名称
get_role_name_by_id(RoleId) ->
    db:get_row(io_lib:format(?sql_role_name_by_id, [RoleId])).

%% 获取玩家货币
get_player_money_data(Id) ->
    db:get_row(io_lib:format(?sql_player_money_data, [Id])).

%% 获取玩家本地头像
get_player_photo_id(RoleId) ->
    [ProfliePhotoId] = db:get_row(io_lib:format(?sql_player_photo_by_role_id, [RoleId])),
    ProfliePhotoId.

%% ---------------------------------------------------------------------------
%% @doc 回写货币数据
-spec update_player_money(OldPlayer, NewPlayer) -> ok when
    OldPlayer   :: #player_status{},    %% 货币变化前的角色数据
    NewPlayer   :: #player_status{}.    %% 货币变化后的角色数据
%% ---------------------------------------------------------------------------
update_player_money(OldPlayer, NewPlayer) ->
    #player_status{gold = Gold, bgold = BGold, fgold = FGold,gfame=GFame, coin = Coin, gcoin = GCoin , honour = Honour, id = PlayerId} = OldPlayer,
    #player_status{gold = NewGold, bgold = NewBGold, fgold = NewFGold,gfame=NewGFame, coin = NewCoin, gcoin = NewGCoin, honour = NewHonour} = NewPlayer,
    %% 1> 更新元宝,绑定元宝和铜钱
    case {NewGold, NewBGold, NewFGold, NewGFame, NewCoin, NewGCoin, NewHonour} of
        {Gold, BGold, FGold, GFame, Coin, GCoin, Honour} -> ok; %% 元宝,绑定元宝和铜钱没有变化，不用更新
        _ -> update_player_money(PlayerId, NewGold, NewBGold, NewFGold, NewGFame, NewCoin, NewGCoin, NewHonour)
    end,
    ok.

%% 更新玩家货币
update_player_money(PlayerId, NewGold, NewBGold, NewFGold, NewGFame, NewCoin, NewGCoin, NewHonour) ->
    Args2 = [NewGold, NewBGold, NewFGold, NewGFame,NewCoin, NewGCoin, NewHonour, PlayerId],
    Sql = io_lib:format(?sql_update_player_money, Args2),
    db:execute(Sql).

%% 保存state数据
update_player_state(Status) ->
    #player_status{id=Id, scene=Scene, x=X, y=Y, battle_attr=BA, quickbar=Quickbar, online = OnlineFlag,
        combat_power=CombatPower, last_be_kill=LastBeKill, passive_skills = PassiveSkill
    } = Status,
    #battle_attr{hp=Hp, pk=Pk, attr = Attr} = BA,
    AttrList = attr_conv:to_attr_tuple_list(Attr),
    StrQuickbar = util:term_to_string(Quickbar),
    StrLastBeKill = ?IF(Hp > 0, util:term_to_string([]), util:term_to_string(LastBeKill)),
    #pk{pk_value = PkValue, pk_status = PkStatus, pk_change_time = PkChangeTime, pk_value_change_time = PkValueChangeTime} = Pk,
    %% 托管玩家不记录场景信息
	case OnlineFlag =:= ?ONLINE_OFF_HOSTING of
        true  ->
            db:execute(io_lib:format(?sql_update_player_state_without_scene, [Hp, StrQuickbar, PkValue, PkStatus,
                PkChangeTime, PkValueChangeTime, CombatPower, StrLastBeKill, util:term_to_string(PassiveSkill), util:term_to_string(AttrList), Id]));
        false ->
            db:execute(io_lib:format(?sql_update_player_state, [Scene, X, Y, Hp, StrQuickbar, PkValue, PkStatus,
                PkChangeTime, PkValueChangeTime, CombatPower, StrLastBeKill, util:term_to_string(PassiveSkill), util:term_to_string(AttrList), Id]))
    end.

%%回写经验值
update_player_exp(Status) ->
    db:execute(io_lib:format(?sql_update_player_exp, [Status#player_status.exp, Status#player_status.id])).

update_player_exp_offline(Status) ->
    #player_status{exp = Exp, exp_list = OldExpList} = Status,
    ExpList = add_exp_list(0, 0, OldExpList, true),
    NewExpList = [{Type, TypeExp}||{Type, TypeExp}<-ExpList, TypeExp=/=0],
    LastExpList = util:term_to_bitstring(NewExpList),
    db:execute(io_lib:format(?sql_update_player_exp_offline, [Exp, LastExpList, Status#player_status.id])).

%% 回写login表数据
update_player_login_offline(Status, Now) ->
    db:execute(io_lib:format(?sql_update_login_offline, [Now, Status#player_status.last_behavior, Status#player_status.id])).

%% 回写历史最高战力
update_hightest_combat_power(Status, CombatPower) ->
    db:execute(io_lib:format(?sql_update_hightest_combat_power, [CombatPower, Status#player_status.id])).

%% 更新在线标识
update_online_flag(RoleId, Flag) ->
    db:execute(io_lib:format(?sql_update_line_flag, [Flag, RoleId])).

%% 更新用户信息_分类(不分线) #player_status
%% @param Id 玩家ID
%% @param AttrKeyValueList 属性列表 [{Key,Value},{Key,Value},...] Key为原子类型，Value为所需参数数据
update_player_info(Id, AttrKeyValueList) when is_list(AttrKeyValueList) ->
    case misc:get_player_process(Id) of
        Pid when is_pid(Pid) -> gen_server:cast(Pid, {'set_data', AttrKeyValueList});
        _ -> false
    end.

%% 更新玩家场景里面的信息
%% @param Id 玩家ID
%% @param AttrKeyValueList 属性列表 [{Key,Value},{Key,Value},...] Key为原子类型，Value为所需参数数据
update_player_scene_info(Id, AttrKeyValueList) when is_list(AttrKeyValueList) ->
    case misc:get_player_process(Id) of
        Pid when is_pid(Pid) -> gen_server:cast(Pid, {'update_player_scene_info', AttrKeyValueList});
        _ -> false
    end.

%% %% 获取玩家职业(通过数据库读取)
%% get_role_career(RoleId) ->
%%     case get_player_low_data(RoleId) of
%%         [_, _, _, Career|_] ->
%%             Career;
%%         _ ->
%%             0
%%     end.

%% -----------------------------------------------------------
%% @doc 按格式(ResultForm)返回#player_status{}中的字段值集合
-spec get_player_info(Id, ResultForm) -> Result when
    Id          :: integer(),                %% 玩家id
    ResultForm  :: integer() | list() | all, %% #player_status.xx | [#player_status.xx, ...] | all
    Result      :: term().                   %% 对应上面的参数的返回值：记录中某个字段的值|记录中某些字段组成的列表|全记录
%% @end
%% -----------------------------------------------------------
get_player_info(Id, ResultForm) ->
    case misc:get_player_process(Id) of
        Pid when is_pid(Pid) ->
            case catch gen_server:call(Pid, {'get_form_data', ResultForm}) of
                {'EXIT', _} -> false;
                Res -> Res
            end;
        _ -> false
    end.

%% 取得在线角色的角色状态(只能用在本节点)
get_online_info(Id) ->
    case ets:lookup(?ETS_ONLINE, Id) of
        []  -> [];
        [R] ->
            case erlang:is_process_alive(R#ets_online.pid) of
                true  -> R;
                false -> []
            end
    end.

%% 获取在线的玩家id列表（包含离线挂机）
get_online_ids()->
    % MatchSpec = ets:fun2ms(fun(#ets_online{id = Id}) -> Id end),
    MatchSpec = [{#ets_online{id = '$1', _ = '_'}, [], ['$1']}],
    ets:select(?ETS_ONLINE, MatchSpec).

%% 获取在线玩家pid列表（包含离线挂机）
get_online_pids() ->
    MatchSpec = [{#ets_online{pid = '$1', _ = '_'}, [], ['$1']}],
    ets:select(?ETS_ONLINE, MatchSpec).

is_exists(Name) ->
    case db:get_one(io_lib:format(?sql_role_id_by_name, [Name])) of
        null   -> false;
        _Other -> true
    end.

%% 检查角色ID是否存在
is_id_exists(RoleId) ->
    case db:get_one(io_lib:format(?sql_role_id_exist, [RoleId])) of
        null -> false;
        _Other -> true
    end.

is_online_global(Id) ->
    case misc:get_player_process(Id) of
        Pid when is_pid(Pid) -> true;
        _ -> false
    end.

get_alive_pid(Id) ->
    case misc:get_player_process(Id) of
        Pid when is_pid(Pid) ->
            case misc:is_process_alive(Pid) of
                true -> Pid;
                false -> false
            end;
        _ -> false
    end.

-define(PLAYER_INFO_DICT, {?MODULE, player_info_dict}).

%% 注册玩家信息
%% InfoMap :: #{id=>Id}
register_player_info_dict(InfoMap) ->
    put(?PLAYER_INFO_DICT, InfoMap).

whereis_player_info_dict() ->
    get(?PLAYER_INFO_DICT).

%% 玩家添加经验
add_mon_dynamic_exp(Status, Mid, Exp) ->
    #player_status{figure = #figure{lv = Lv}} = Status,
    case data_mon_dynamic:get(Mid, Lv) of
        #mon_dynamic{exp = DExp} when DExp > 0 -> add_exp(Status, DExp, role_kill_mon, []);
        _ -> add_exp(Status, Exp, role_kill_mon, [])
    end.

%% 增加人物经验
add_exp(Status, Exp) ->
    add_exp(Status, Exp, 0, unkown, []).

add_exp(Status, Exp, ProduceType) ->
    add_exp(Status, Exp, 0, ProduceType, []).

add_exp(Status, Exp, ProduceType, KeyValueList) ->
    add_exp(Status, Exp, 0, ProduceType, KeyValueList).

%% 增加人物经验
%% @param  OldStatus    增加经验前的#player_status{}
%% @param  AddExp       增加的经验
%% @param  State        0的时候收益正常,1为受防沉迷收益
%% @param  ProduceType  生成类型atom()
%% @param  KeyValueList 经验KeyValue参数列表 [{Key, Value}|...]
%%         Key :: 亲密度intimacy | 副本id:dun_id
%% @return #player_status{}
add_exp(OldStatus, AddExp, _State, ProduceType, KeyValueList) ->
    #player_status{id = RoleId, figure = _Figure, exp_ratio = ExpRatio} = OldStatus,
    %% 计算总的经验
    {ExpAdd, ExpAddRatio}
    = case ProduceType of
        role_kill_mon -> %% 杀怪
            {round(AddExp*ExpRatio), round(ExpRatio*100)};
        %% 其他类型
        _ -> {round(AddExp), 0}
    end,
    %% 飘字增加经验
    ExpType = data_goods:get_produce_type(ProduceType),
    lib_server_send:send_to_uid(RoleId, pt_130, 13036, [ExpType, ExpAdd, ExpAddRatio]),

    %% 经验海记录经验收益
    case exp_sea_logic:is_exp_sea_scene(OldStatus#player_status.scene) of
        true when ProduceType == role_kill_mon -> exp_sea_logic:add_exp(RoleId, ExpAdd);
        _ -> ignore
    end,

    %% 添加经验
    NewStatus = add_exp_helper(OldStatus, ExpAdd, ProduceType, KeyValueList),
    NewStatus.

%% 玩家升级防御(2秒内最多升10级)
-define(LIMIT_TIME, 2).         % 单位限制时间：2秒
-define(LIMIT_NUM(ProduceType), % 限制数量：默认20级
    case ProduceType of
        gm -> 999;              % gm无限制
        exp_goods -> 50;
        onhook_offline_reward -> 50;
        onhook_online_reward -> 50;
        resource_back -> 50;
        _ -> 20
    end).
-define(LVUP_LIMIT_TIME, {?MODULE, lvup_limit}).

-define(EXP_LIST_LEN, 20).     % 经验列表长度

add_exp_helper(Status, Exp, ProduceType, Args) ->
    NowTime = utime:unixtime(),
    #player_status{id = Id, figure = Figure, exp = OldExp, exp_list = OldExpList, skill = Skill} = Status,
    #figure{lv = OldLv, turn = Turn} = Figure,
    LvMaxExp = data_exp:get(OldLv),
    MaxLv =  data_exp:get_max_lv(),
    ExpSum = Exp + OldExp,
    ExpType = data_goods:get_produce_type(ProduceType),
    if
        % %% 500级，没有4转，就只加经验，不升级
        % OldLv >= ?DEF_TURN_4_LV andalso Turn < ?DEF_TURN_4 ->
        %     {ok, BinData} = pt_130:write(13002, [ExpSum]),
        %     lib_server_send:send_to_sid(Status#player_status.sid, BinData),
        %     %% 缓存经验累加次数
        %     case get("lib_player_add_exp") of
        %         undefined ->
        %             LimitTimes = urand:rand(100, 150),
        %             put("lib_player_add_exp", {1, LimitTimes});
        %         {Times, LimitTimes} when Times < LimitTimes -> put("lib_player_add_exp", {Times+1, LimitTimes});
        %         _ ->
        %             db:execute(io_lib:format(<<"update `player_high` set `exp`=~w where `id`=~w ">>, [ExpSum, Id])),
        %             erase("lib_player_add_exp")
        %     end,
        %     Status#player_status{exp = ExpSum};

        %% ---------- 未升级 ----------
        %% ---------- 未达升级经验值
        OldLv >= MaxLv orelse ExpSum < LvMaxExp  ->
            {ok, BinData} = pt_130:write(13002, [ExpSum]),
            lib_server_send:send_to_sid(Status#player_status.sid, BinData),
            %% 缓存经验累加次数
            case get("lib_player_add_exp") of
                undefined ->
                    LastExpList = add_exp_list(ExpType, Exp, OldExpList, false),
                    LimitTimes = urand:rand(100, 150),
                    put("lib_player_add_exp", {1, LimitTimes});
                {Times, LimitTimes} when Times < LimitTimes ->
                    LastExpList = add_exp_list(ExpType, Exp, OldExpList, false),
                    put("lib_player_add_exp", {Times+1, LimitTimes});
                _ ->
                    LastExpList = add_exp_list(ExpType, Exp, OldExpList, true),
                    db:execute(io_lib:format(<<"update `player_high` set `exp`=~w, `exp_list`='~s' where `id`=~w ">>,
                            [ExpSum, util:term_to_bitstring(LastExpList), Id])),
                    erase("lib_player_add_exp")
            end,
            Status#player_status{exp = ExpSum, exp_list = LastExpList};

        %% ---------- 已升级 ----------
        true ->
            case get(?LVUP_LIMIT_TIME) of
                {LastLvUpLimitTime, UpNum} when NowTime - LastLvUpLimitTime =< ?LIMIT_TIME ->
                    NewUpNum = UpNum + 1,
                    put(?LVUP_LIMIT_TIME, {LastLvUpLimitTime, NewUpNum});
                _ ->
                    NewUpNum = 1,
                    put(?LVUP_LIMIT_TIME, {NowTime, NewUpNum})
            end,
            LimitNum = ?LIMIT_NUM(ProduceType),
            CanUp = case ProduceType of
                %% 2秒内最多升指定的级数
                _ when NewUpNum=<LimitNum -> true;
                _ -> false
            end,
            %% 是否升级
            case CanUp of
                false ->
                    ?ERR("role lv_up abnormal:~p~n", [{Id, OldLv, ProduceType, Exp}]),
                    Status;
                true ->
                    LeftExp = ExpSum - LvMaxExp,
                    Lv = OldLv + 1,
                    %% 技能点
                    OldSkillPoint = Skill#status_skill.point,
                    SkillPoint = ?IF(Turn>=5 andalso Lv>500, OldSkillPoint+1, OldSkillPoint),

                    %% 等级升级处理
                    ExpLimit = data_exp:get(Lv),
                    PSExp = Status#player_status{exp = LeftExp, exp_lim = ExpLimit, exp_list = [], figure = Figure#figure{lv=Lv}},
                    {ok, BinData} = pt_130:write(13003, [Lv, LeftExp, ExpLimit]),
                    lib_server_send:send_to_sid(PSExp#player_status.sid, BinData),

                    %% 技能点处理
                    Fun = fun() ->
                            ?IF(SkillPoint == OldSkillPoint, skip,
                                db:execute(io_lib:format(<<"update `player_state` set skill_extra_point = ~w where id=~w ">>, [SkillPoint, Id]))),
                            db:execute(io_lib:format(<<"update `player_high` set exp=~w, exp_list='~s' where id=~w ">>, [LeftExp, util:term_to_bitstring([]), Id])),
                            db:execute(io_lib:format(<<"update `player_low` set lv = ~w, onhook_lv = ~w where id=~w ">>, [Lv, Lv, Id]))
                    end,
                    db:transaction(Fun),

                    %% 写进程字典
                    OldGoodsStatus = lib_goods_do:get_goods_status(),
                    lib_goods_do:set_goods_status(OldGoodsStatus#goods_status{player_lv = Lv}),


                    PSSkillPoint = PSExp#player_status{skill = Skill#status_skill{point = SkillPoint}},

                    %% 派发升级事件之 全属性计算前
                    {ok, PSEventLvBf} = lib_player_event:dispatch(PSSkillPoint, ?EVENT_LV_UP_BF_COUNT_ATTR),

                    %% 人物属性计算
                    PSCountAttr = count_player_attribute(PSEventLvBf),

                    %% 派发升级事件之 全属性计算后
                    {ok, PSEventLv} = lib_player_event:dispatch(PSCountAttr, ?EVENT_LV_UP),
                    {ok, PSAchievement} = lib_achievement_api:lv_up_event(PSEventLv, []),

                    %% 升级满血
                    NewBattleAttr = PSAchievement#player_status.battle_attr,
                    NewPSEnd = PSAchievement#player_status{
                        battle_attr = NewBattleAttr#battle_attr{hp = NewBattleAttr#battle_attr.hp_lim}
                    },
                    %% 通知到场景更新血量
                    mod_scene_agent:handle_event(NewPSEnd, #event_callback{type_id = ?EVENT_LV_UP}),
                    send_attribute_change_notify(NewPSEnd, ?NOTIFY_ATTR),

                    %% 写入日志
                    LastExpList = add_exp_list(ExpType, Exp, OldExpList, true),
                    lib_log_api:log_uplv(Id, Lv, Turn, ExpType, ExpSum, LastExpList, LvMaxExp, NewPSEnd#player_status.combat_power),
                    %% 连升多级
                    NextLvMaxExp = data_exp:get(Lv),
                    case NextLvMaxExp > LeftExp of
                        true  -> NewPSEnd;
                        false -> add_exp_helper(NewPSEnd#player_status{exp = 0}, LeftExp, ProduceType, Args)
                    end
                    %% 请不要在后面加代码，这里是尾递归
            end
    end.

%% 增加等级经验列表
add_exp_list(ExpType, Exp, OldExpList, IsSub) ->
    case lists:keyfind(ExpType, 1, OldExpList) of
        {ExpType, TypeOldExp} -> skip;
        _ -> TypeOldExp = 0
    end,
    ExpList = lists:keystore(ExpType, 1, OldExpList, {ExpType, TypeOldExp + Exp}),
    if
        IsSub == true -> lists:sublist(lists:reverse(lists:keysort(2, ExpList)), ?EXP_LIST_LEN);
        true -> ExpList
    end.

%% 扣除玩家经验
deduct_exp(Status, Exp, Type, About) ->
    NewExp = max(0, Status#player_status.exp - Exp),
    {ok, BinData} = pt_130:write(13002, [NewExp]),
    lib_server_send:send_to_sid(Status#player_status.sid, BinData),
    Status1 = Status#player_status{exp = NewExp},
    update_player_exp(Status1),
    lib_log_api:log_consume_exp(Type, Status, Status1, About),
    Status1.

%% 经验是否满足
is_enough_exp(PS, Exp) ->
    PS#player_status.exp >= Exp.

%% 获取传奇等级
get_legend_lv(PS) when is_record(PS, player_status)->
    #player_status{figure = Figure} = PS,
    get_legend_lv(Figure);
get_legend_lv(Figure) ->
    #figure{lv = Lv, turn = Turn} = Figure,
    if
        Lv >= 370 andalso Turn>= 4 -> max(Lv - 370, 0);
        true -> 0
    end.

%% 人物属性计算
%% 各模块的属性保存时，应当使用列表[{K,V}]存储在ps中
count_player_attribute(PlayerStatus) ->
    count_player_attribute(PlayerStatus, 0, undefined, []).
count_player_attribute(PlayerStatus, ModId) ->
    count_player_attribute(PlayerStatus, ModId, undefined, []).
count_player_attribute(PlayerStatus, ModId, Args) when is_atom(ModId) ->
    count_player_attribute(PlayerStatus, ModId, undefined, Args);
count_player_attribute(PlayerStatus, ModId, OperationType) ->
    count_player_attribute(PlayerStatus, ModId, OperationType, []).

%% 需要记录日志的属性计算选用这个接口，设定对应的ModId和OperationType
-spec count_player_attribute(PlayerStatus, ModId, OperationType, Args) -> NewPlayerStatus when
    PlayerStatus        :: #player_status{},           %% 玩家Ps
    ModId               :: integer() | atom(),         %% 成长线模块id(如降神440)，当ModId为后者或者为0时不记录日志
    OperationType       :: atom(),                     %% 操作类型(如降神升阶 up_god)
    Args                :: any(),                      %% 日志中要额外记录的东西，或者一些其他的参数
    NewPlayerStatus     :: #player_status{}.           %% 新的玩家Ps
count_player_attribute(PlayerStatus, ModId, _OperationType, Args) ->
    NewPlayerStatus = do_count_player_attribute(PlayerStatus, ModId, Args),
    NewPlayerStatus.

do_count_player_attribute(PlayerStatus, ModId, _Args) ->
    #player_status{
        pid = Pid, combat_power = OldCombatPower,
        figure = Figure, battle_attr = BA, buff_attr = GoodsBuffAttrs,
        dsgt_status = #dsgt_status{attr = DsgtAttr},            % 称号属性
        skill = #status_skill{                                  % 任务被动技能和天赋被动技能属性
            skill_attr = SkillAttr, skill_power = SkillPower
            ,skill_talent_power = SkillTalentPower
            ,skill_talent_attr = SkillTalentAttr
            ,hp_resume_spcl_add = HpResumeSpclAdd
        },
        role_liveness = #role_liveness{attr = LivenessAttr},    % 活跃度属性
        flower = #flower{attr = FlowerAttr},                    % 鲜花属性
        dress_up = #status_dress_up{attr = DressUpAttr},        % 个性装扮属性
        status_pet = #status_pet{attr_list = PetAttr, extra_power = PetSkillPower},         % 宠物属性
        guild = #status_guild{attr = GuildAttr},                % 公会技能属性
        % degree = #degree{attr_list = DegreeAttr},               % 境界学位属性
        status_child = #status_child{attr_list = ChildAttr, extra_power = ChildSkillPower}, % 孩子属性
        fishing = #fishing{attr = FishAttr},                    % 钓鱼 鱼鉴属性
        limit_goal_soul = #limit_goal_soul_status{sum_attr = LimitGoalAttr},    % 限时目标星魂属性
        role_foster = #role_foster{attr=FosterAttr}
        ,profile_photo_info = #profile_photo_info{pp_attr = ProfilePAttrL}      % 头像属性
        ,role_fashion = #role_fashion{fashion_attr = FashionAttr}
        ,role_achievement = #role_achievement{attr = AchievementAttr}
    } = PlayerStatus,
    %% 速度属性
    SpeedAttr = [{?SPEED, ?SPEED_VALUE}],
    %% 转职属性
    TransformAttr = transform:get_transform_attr(PlayerStatus#player_status.transform_data),
    %% 羁绊系统
    MateAttr = mate_api:get_mate_attr(PlayerStatus),
    %% 装备系统
    EquipmentAttr = equipment_api:get_attr(PlayerStatus),
    %% 吞噬属性
    DevourAttr = goods_devour:get_attr(PlayerStatus),

    BadgeAttr  = lib_guild_badge:get_badge_attr(PlayerStatus),

    VisusAttr  = lib_role_visus:get_visus_attr(PlayerStatus),

    NucleonAttr = lib_role_nucleon:get_nucleon_attr(PlayerStatus),

    FosterSkinAttr = lib_role_foster_skin:get_foster_skin_attr(PlayerStatus),

    GalaxyAttr = lib_role_galaxy:get_galaxy_attr(PlayerStatus),

    %% 唤神
    GodAttr = god_api:get_attr(PlayerStatus, _Args),
    %% 装备收集
    EquipmentCollect = equipment_collect:get_attr(PlayerStatus),
    %% 战魂系统
    WarSoulAttr = warsoul_api:get_attr(PlayerStatus),
    %% 魂力系统
    UraniumAttr = lib_role_uranium:get_attr(PlayerStatus),
    %% 军衔属性
    LegionAttr  = lib_role_legion:get_attr(PlayerStatus),
    %% 时装工坊属性
    WorkroomAttr = lib_workroom:get_attr(PlayerStatus),

    %% ================================= 汇总所有的列表的总属性 =================================
    AllAttrList = [
        SpeedAttr, DsgtAttr, FashionAttr, SkillAttr, SkillTalentAttr,
        FlowerAttr, DressUpAttr,AchievementAttr,
        GuildAttr, TransformAttr, GodAttr,
        FishAttr,LivenessAttr, EquipmentCollect,
        LimitGoalAttr,FosterAttr, ProfilePAttrL, MateAttr, PetAttr, EquipmentAttr,
        ChildAttr,VisusAttr, DevourAttr,BadgeAttr,NucleonAttr,FosterSkinAttr,GalaxyAttr,
        WarSoulAttr,UraniumAttr,LegionAttr, WorkroomAttr
    ],
    AttrList = lib_player_attr:add_attr(list, AllAttrList),
    %% 等级基础属性
    LvAttr = base_lv_attr(Figure#figure.lv),
    LvAttrList = lib_player_attr:count_lv_attr(LvAttr, [AttrList]),
    NewAttrList = lib_player_attr:add_attr(list, [AttrList, LvAttrList]),
    OriginAttrTotal = attr_conv:to_attr_record(NewAttrList),
    #attr{att = Att, hp = Hp, def = Def, hit = Hit, dodge = Dodge, crit = Crit, ten = Ten,
        final_hurt_add = FinalHurtAdd, final_hurt_del = FinalHurtDel,
        hp_resume = HpResume, hp_resume_ratio = HpResumeRatio
    } = OriginAttrTotal,
    TotalAttr = lib_player_attr:attr_count_with_extra(OriginAttrTotal),
    %% 计算出纯属性部分的战力
    AttrPower = lib_player_attr:calc_attr_power(TotalAttr),
    AttrNotCalcPower = lib_player_attr:add_attr(record, [GoodsBuffAttrs]),
    #attr{att = Att2, hp = Hp2, def = Def2, hit = Hit2, dodge = Dodge2, crit = Crit2, ten = Ten2,
        final_hurt_add = FinalHurtAdd2, final_hurt_del = FinalHurtDel2,
        att_add_ratio = AttAddR2, hp_add_ratio = HpAddR2, def_add_ratio = DefAddR2,
        hit_add_ratio = HitAddR2, dodge_add_ratio = DodgeAddR2,
        crit_add_ratio = CritAddR2, ten_add_ratio = TenAddR2,
        final_hurt_add_ratio = FinalHurtAddR2, final_hurt_del_ratio = FinalHurtDelR2
    } = AttrNotCalcPower,
    %% 不计算战力的百分比属性折算成具体值
    NoPowerRAttrs = [
        {?ATT,              round((Att          + Att2          ) * AttAddR2/?RATIO_MAX)},
        {?HP,               round((Hp           + Hp2           ) * HpAddR2/?RATIO_MAX)},
        {?DEF,              round((Def          + Def2          ) * DefAddR2/?RATIO_MAX)},
        {?HIT,              round((Hit          + Hit2          ) * HitAddR2/?RATIO_MAX)},
        {?DODGE,            round((Dodge        + Dodge2        ) * DodgeAddR2/?RATIO_MAX)},
        {?CRIT,             round((Crit         + Crit2         ) * CritAddR2/?RATIO_MAX)},
        {?TEN,              round((Ten          + Ten2          ) * TenAddR2/?RATIO_MAX)},
        {?FINAL_HURT_ADD,   round((FinalHurtAdd + FinalHurtAdd2 ) * FinalHurtAddR2/?RATIO_MAX)},
        {?FINAL_HURT_DEL,   round((FinalHurtDel + FinalHurtDel2 ) * FinalHurtDelR2/?RATIO_MAX)}
    ],
    %% 总属性 = 战力部分全部属性 + 非战力部分基础属性 + (战力部分基础属性 + 非战力部分基础属性) * 非战力部分百分比属性
    TotalAttrForBattle = lib_player_attr:add_attr(record, [TotalAttr, GoodsBuffAttrs, NoPowerRAttrs]),
    %% 属性值检测转换为整数
    FinialAttr  = lib_player_attr:ensure_attr_int(TotalAttrForBattle),
    #attr{hp = HpLim, speed = LastSpeed} = FinialAttr,

    %% 血量修正
    #battle_attr{hp = OldHp, hp_lim = _OldHpLim} = BA,
    RealHp = lib_player_attr:calc_hp_while_hp_lim_change(OldHp, _OldHpLim, HpLim),
    %% 未计算属性额外加成之前的部分基础属性列表，存储便于发送给前端
    BaseAttrMap = #{
        ?ATT => round(Att), ?HP => round(Hp), ?DEF => round(Def), ?HIT => round(Hit),
        ?DODGE => round(Dodge), ?CRIT => round(Crit), ?TEN => round(Ten),
        ?FINAL_HURT_ADD => round(FinalHurtAdd), ?FINAL_HURT_DEL => round(FinalHurtDel)
    },
    HpResumeAdd = lib_player_attr:calc_hp_resume_add(HpLim, HpResume, HpResumeRatio, HpResumeSpclAdd),
    TmpBA = BA#battle_attr{
        hp = RealHp, hp_lim = HpLim, speed = LastSpeed, attr = FinialAttr,
        hp_resume_add = HpResumeAdd, base_attr_map = BaseAttrMap
    },
    TmpPS = PlayerStatus#player_status{battle_attr = TmpBA, role_base_attr = {AttrPower, OriginAttrTotal}},
    %% 回调计算各模块的属性影响相关战力 及 技能固定战力
    %% （历史原因，部分系统的计算并不在这里，后续添加注意统一）
    {PSCalcModulePower, ModuleSkillPowerAcc} = lib_player_attr:update_module_power(TmpPS, AttrPower, OriginAttrTotal),
    %% 战力汇总
    CombatPower = round(AttrPower + SkillPower + SkillTalentPower + PetSkillPower + ChildSkillPower + ModuleSkillPowerAcc),
    %% 机器人额外处理
    NewBA = case lib_robot:is_robot(PSCalcModulePower) of
        true ->
            ExtraHp  = 10000,
            ExtraAtt = 100,
            RobotFixAttr = FinialAttr#attr{hp = HpLim + ExtraHp, att = FinialAttr#attr.att + ExtraAtt},
            TmpBA#battle_attr{hp = RealHp + ExtraHp, hp_lim = HpLim + ExtraHp, attr = RobotFixAttr};
        false ->
            TmpBA
    end,
    PSCalcExpRatio = update_exp_add_ratio(PSCalcModulePower),
    PSCalcExpDropRatio = update_exp_dun_drop_ratio(PSCalcExpRatio),
    PSUpdatePower = PSCalcExpDropRatio#player_status{
        combat_power = CombatPower,
        battle_attr = NewBA,
        figure = Figure#figure{power = CombatPower},
        passive_skills = lib_skill_api:include_battle_passive_skill(PSCalcExpDropRatio)
    },
    NewPS = count_player_speed(PSUpdatePower),
    case CombatPower =/= OldCombatPower andalso (self() =:= Pid orelse misc:is_process_alive(Pid)) of
        true  ->
            %% 派发战力改变事件
            CallbackData = #callback_combat_power_data{
                type = ModId,
                old_combat_power = OldCombatPower,
                combat_power = CombatPower
            },
            {ok, NewPS2} = lib_player_event:dispatch(NewPS, ?EVENT_COMBAT_POWER, CallbackData),
            NewPS2;
        false ->
            NewPS
    end.

%% 计算学到的技能对其他功能的属性影响
count_func_attribute(SkillId, SkillLv, Ps)->
    case data_skill:get(SkillId,  SkillLv) of
        #skill{lv_data = LvData} when is_record(LvData, skill_lv)->
            #skill_lv{base_attr = BaseAttr} = LvData,
            count_func_attribute(BaseAttr, Ps);
        _ -> Ps
    end.

count_func_attribute([], Ps)-> Ps;
count_func_attribute([{FuncId, _, _ }|BaseAttr], Ps)->
    NewPs = case FuncId of
        _ ->
            Ps
    end,
    count_func_attribute(BaseAttr, NewPs).

%% 玩家等级属性
base_lv_attr(RoleLv)->
    data_lv_attr:get(RoleLv).

%% 获取其他的属性值
get_other_attr(_AttrList, [], ReturnList) -> lists:reverse(ReturnList);
get_other_attr(AttrList, [AttrId|T], ReturnList) ->
    case lists:keyfind(AttrId, 1, AttrList) of
        false -> Value = 0;
        {_, Value} -> skip
    end,
    get_other_attr(AttrList, T, [Value|ReturnList]).

%% 计算玩家速度
count_player_speed(PS)->
    #player_status{
        id = RoleId,
        battle_attr=BA
    } = PS,
    LastPS = update_speed(PS),
    LastSpeed = LastPS#player_status.battle_attr#battle_attr.speed,
    case BA#battle_attr.speed /= LastSpeed of
        true ->
            #player_status{
                scene = SceneId, scene_pool_id = PoolId, copy_id = CopyId,
                x = X, y = Y
            } = LastPS,
            lib_scene:change_speed(RoleId, SceneId, PoolId, CopyId, X, Y, LastSpeed, ?BATTLE_SIGN_PLAYER);
        false -> skip
    end,
    LastPS.


update_speed(PS) ->
    #player_status{
        battle_attr=BA,
        figure = #figure{is_ride=IsRide}
    } = PS,
    IsGuildWarEscort = guild_war_api:is_escort(PS),
    IsConvoy = convoy_api:is_convoy_status(PS),
    LastSpeed = if
                    IsGuildWarEscort -> %% 采集押运
                        300;
                    IsConvoy -> %% 护送
                        300;
                    true ->
                        %% 基础速度
                        BaseSpeed = BA#battle_attr.attr#attr.speed,
                        %% 坐骑骑乘速度
                        MountSpeed = 400*IsRide,
                        %% ====汇总===
                        round(BaseSpeed + MountSpeed)
                end,
    PS#player_status{battle_attr = BA#battle_attr{speed=LastSpeed}}.

%% 计算玩家历史最高战斗力
count_hightest_combat_power(PS) ->
    UpdateCd = 60,
    UpdateLv = 15,
    NowTime = utime:unixtime(),
    #player_status{
        id = RoleId,
        figure = #figure{lv = Lv},
        combat_power = CombatPower,
        hightest_combat_power = HightCombatPower,
        hightest_combat_power_cd = HightCombatPowerCd
    } = PS,
    if
        Lv >= UpdateLv andalso NowTime >= HightCombatPowerCd andalso CombatPower > HightCombatPower ->
            NewPS = PS#player_status{
                hightest_combat_power = CombatPower,
                hightest_combat_power_cd = NowTime + UpdateCd
            },
            update_hightest_combat_power(NewPS, CombatPower),
            svr_vitality_local_mgr:power_up(RoleId, CombatPower, NowTime),
            NewPS;
        true -> PS
    end.

% 升级后，将当前战力构成写入日志
handle_event(PS, #event_callback{type_id = ?EVENT_LV_UP}) ->
    %write_log_combat_uplv(PS),
    {ok, PS};
%% 处理回调事件
handle_event(PS, #event_callback{type_id = ?EVENT_COMBAT_POWER}) ->
    NewPS = count_hightest_combat_power(PS),
    {ok, NewPS};
%% 处理回调事件
handle_event(PS, #event_callback{type_id = ?EVENT_CLIENT_LOGIN_SUCCESS}) ->
    case ?IS_DEV_SERVER andalso clusters_api:is_connected_center() of
        true -> lib_chat:send_TV({player, PS#player_status.id}, ?MOD_NONE, 3, []);
        false -> skip
    end,
    {ok, PS};
handle_event(PS, #event_callback{type_id = ?EVENT_CENTER_CONNECTED}) ->
    case ?IS_DEV_SERVER andalso clusters_api:is_connected_center() of
        true -> lib_chat:send_TV({player, PS#player_status.id}, ?MOD_NONE, 4, []);
        false -> skip
    end,
    {ok, PS};
handle_event(PS, #event_callback{type_id = ?EVENT_CENTER_DISCONNECT}) ->
    case ?IS_DEV_SERVER andalso clusters_api:is_connected_center() == false of
        true -> lib_chat:send_TV({player, PS#player_status.id}, ?MOD_NONE, 5, []);
        false -> skip
    end,
    {ok, PS};
%% @doc 转职之后激活默认转职头像(不存数据库) @end
handle_event(PS, #event_callback{type_id = ?EVENT_TURN_UP}) when is_record(PS, player_status) ->
    #player_status{sid = Sid, figure = Figure, profile_photo_info = #profile_photo_info{photo_list = PhotoList}
    } = PS,
    #figure{career = Career} = Figure,
    TransformId = transform:get_transform_id(PS),
    case data_transform:get_career_transform_image(Career, TransformId) of
        PhotoId when is_integer(PhotoId) -> ok;
        _ -> PhotoId = 0
    end,
    {ColorList, NotOpenNum} = calc_all_profile_photo(PhotoList, Career, TransformId),
    {ok, PS1} = lib_wardrobe_active:active_picture(PS, ColorList,NotOpenNum, PhotoId),
    %% 推送最新头像给前端
    F = fun(TTransformId, TList) ->
        case data_transform:get_career_transform_image(Career, TTransformId) of
            TPhotoId when is_integer(TPhotoId) ->
                case lists:member(TPhotoId, TList) of
                    true -> TList;
                    _ -> [TPhotoId|TList]
                end;
            _ -> TList
        end end,
    AllPhotoList = lists:foldl(F, PhotoList, lists:seq(0, TransformId)),
    {ok, BinData} = pt_130:write(13088, [AllPhotoList]),
    lib_server_send:send_to_sid(Sid, BinData),
    {ok, PS1};
handle_event(PS, #event_callback{type_id = ?EVENT_VIP_TIME_OUT}) when is_record(PS, player_status) ->
    ExpDropPS = lib_player:update_exp_dun_drop_ratio(PS),
    NewPS     = lib_player:update_exp_add_ratio(ExpDropPS),
    {ok, NewPS};
handle_event(PS, #event_callback{type_id = ?EVENT_VIP}) when is_record(PS, player_status) ->
    ExpDropPS   = lib_player:update_exp_dun_drop_ratio(PS),
    NewPS  = lib_player:update_exp_add_ratio(ExpDropPS),
    {ok, NewPS};
handle_event(PS, _EC) ->
    ?ERR("unkown event_callback:~p", [_EC]),
    {ok, PS}.

%%----------------------------------------------------------------------------------
%% @doc 发送相关玩家值给客户端更新
-spec send_attribute_change_notify(Status, ChangeReason) -> ok when
    Status :: #player_status{},         %% 玩家进程状态
    ChangeReason :: integer()|list().   %% 更新原因
%%----------------------------------------------------------------------------------
send_attribute_change_notify(Status, ?NOTIFY_ATTR) ->
    #player_status{
        sid = Sid, combat_power = CombatPower,
        battle_attr  = BattleAttr
    } = Status,
    BaseAttrList = maps:to_list(BattleAttr#battle_attr.base_attr_map),
    {ok, BaseAttrBin} = pt_130:write(13032, [BaseAttrList]),
    {ok, BattleAttrBin} = pt_130:write(13033, [CombatPower, BattleAttr]),
    lib_server_send:send_to_sid(Sid, <<BaseAttrBin/binary, BattleAttrBin/binary>>);

send_attribute_change_notify(Status, ?NOTIFY_PK) ->
    #player_status{
        sid = Sid,
        battle_attr = #battle_attr{pk = #pk{pk_value=PkValue, pk_value_change_time = PkValueChangeTime}}
    } = Status,
    lib_server_send:send_to_sid(Sid, pt_130, 13034, [PkValue, PkValueChangeTime]);

%% 货币改变
send_attribute_change_notify(Status, ?NOTIFY_MONEY) ->
    #player_status{
        sid  = Sid, gold = Gold, bgold = BGold, fgold = FGold, coin = Coin, gfame=GFame
        ,honour = Honour
    } = Status,
    {ok, BinData} = pt_130:write(13006, [Coin, Gold, BGold, FGold, GFame, Honour]),
    lib_server_send:send_to_sid(Sid, BinData);

send_attribute_change_notify(Status, ReasonList) when is_list(ReasonList) ->
    [send_attribute_change_notify(Status, Reason) || Reason <- ReasonList],
    ok.

%% ---------------------------------------------------------------------------
%% @doc 角色进程cast方式执行MFA 跨节点
-spec apply_cast(Node, PlayerPid, CastType, HandleOffline, Moudle, Method, Args) -> Res when
    Node        :: atom(),
    PlayerPid   :: integer() | pid(),   %% 玩家Id或者玩家进程pid
    CastType    :: integer(),           %% Cast类型：?APPLY_CAST|?APPLY_CAST_STATUS|?APPLY_CAST_SAVE
    %% ?APPLY_CAST       ::1 进程cast方式执行MFA
    %% ?APPLY_CAST_STATUS::2 进程cast方式执行MFA，默认添加#player_status{}作为A的第一个参数
    %% ?APPLY_CAST_SAVE  ::3 进程cast方式执行MFA，默认添加#player_status{}作为A的第一个参数，且保存新的#status{}
    HandleOffline::integer(),           %% 是否处理离线情况
    Moudle      :: atom(),              %% 模块名
    Method      :: atom(),              %% 方法名
    Args        :: term(),              %% 方法实参
    Res         :: ok | skip.           %% 结果码
%% ---------------------------------------------------------------------------
apply_cast(Node, PlayerPid, CastType, HandleOffline, Moudle, Method, Args) ->
    if
        Node =:= node() orelse Node =:= undefined orelse Node =:= none ->
            apply_cast(PlayerPid, CastType, HandleOffline, Moudle, Method, Args);
        true ->
            rpc:cast(Node, ?MODULE, apply_cast, [PlayerPid, CastType, HandleOffline, Moudle, Method, Args])
    end.

%% ---------------------------------------------------------------------------
%% @doc 角色进程cast方式执行MFA
-spec apply_cast(PlayerPid, CastType, Moudle, Method, Args) -> Res when
    PlayerPid   :: integer() | pid(),   %% 玩家Id或者玩家进程pid
    CastType    :: integer(),           %% Cast类型：?APPLY_CAST|?APPLY_CAST_STATUS|?APPLY_CAST_SAVE
    %% ?APPLY_CAST       ::1 进程cast方式执行MFA
    %% ?APPLY_CAST_STATUS::2 进程cast方式执行MFA，默认添加#player_status{}作为A的第一个参数
    %% ?APPLY_CAST_SAVE  ::3 进程cast方式执行MFA，默认添加#player_status{}作为A的第一个参数，且保存新的#status{}
    Moudle      :: atom(),              %% 模块名
    Method      :: atom(),              %% 方法名
    Args        :: term(),              %% 方法实参
    Res         :: ok | skip.           %% 结果码
%% ---------------------------------------------------------------------------
apply_cast(PlayerPid, CastType, Moudle, Method, Args) ->
    apply_cast(PlayerPid, CastType, ?NOT_HAND_OFFLINE, Moudle, Method, Args).

%% ---------------------------------------------------------------------------
%% @doc 角色进程cast方式执行MFA
-spec apply_cast(PlayerPid, CastType, HandleOffline, Moudle, Method, Args) -> Res when
    PlayerPid   :: integer() | pid(),   %% 玩家Id或者玩家进程pid
    CastType    :: integer(),           %% Cast类型：?APPLY_CAST|?APPLY_CAST_STATUS|?APPLY_CAST_SAVE
    %% ?APPLY_CAST       ::1 进程cast方式执行MFA
    %% ?APPLY_CAST_STATUS::2 进程cast方式执行MFA，默认添加#player_status{}作为A的第一个参数
    %% ?APPLY_CAST_SAVE  ::3 进程cast方式执行MFA，默认添加#player_status{}作为A的第一个参数，且保存新的#status{}
    HandleOffline::integer(),           %% 是否处理离线情况
    %% ?NOT_HAND_OFFLINE ::0 不处理离线情况
    %% ?HAND_OFFLINE     ::1 处理离线情况
    Moudle      :: atom(),              %% 模块名
    Method      :: atom(),              %% 方法名
    Args        :: term(),              %% 方法实参
    Res         :: ok | skip.           %% 结果码
%% ---------------------------------------------------------------------------
apply_cast(PlayerPid, CastType, HandleOffline, Moudle, Method, Args) when is_integer(PlayerPid) ->
    Pid = misc:get_player_process(PlayerPid),
    case misc:is_process_alive(Pid) of
        true ->
            gen_server:cast(Pid, {apply_cast, CastType, Moudle, Method, Args});
        _  when HandleOffline == ?HAND_OFFLINE ->
            lib_offline_api:apply_cast(PlayerPid, Moudle, Method, Args);
        _ ->
            skip
    end;
apply_cast(PlayerPid, CastType, _HandleOffline, Moudle, Method, Args) ->
    case misc:is_process_alive(PlayerPid) of
        true ->
            gen_server:cast(PlayerPid, {apply_cast, CastType, Moudle, Method, Args});
        %% false when HandleOffline == ?HAND_OFFLINE ->
        %%     lib_offline_api:apply_cast(PlayerPid, Moudle, Method, Args);
        false ->
            skip
    end.

%% ---------------------------------------------------------------------------
%% @doc 角色进程cast方式执行MFA
-spec apply_call(PlayerPid, CastType, Moudle, Method, Args) -> Res when
    PlayerPid   :: integer() | pid(),   %% 玩家Id或者玩家进程pid
    CastType    :: integer(),           %% Call类型：?APPLY_CALL|?APPLY_CALL_STATUS|?APPLY_CALL_SAVE
    %% ?APPLY_CALL       ::1 进程call方式执行MFA
    %% ?APPLY_CALL_STATUS::2 进程call方式执行MFA，默认添加#player_status{}作为A的第一个参数
    %% ?APPLY_CALL_SAVE  ::3 进程call方式执行MFA，默认添加#player_status{}作为A的第一个参数，且保存新的#status{}
    Moudle      :: atom(),              %% 模块名
    Method      :: atom(),              %% 方法名
    Args        :: term(),              %% 方法实参
    Res         :: skip | term().       %% 返回值
%% ---------------------------------------------------------------------------
apply_call(PlayerPid, CallType, Moudle, Method, Args) ->
    apply_call(PlayerPid, CallType, Moudle, Method, Args, sys_timeout).

apply_call(PlayerPid, CallType, Moudle, Method, Args, Timeout) when is_pid(PlayerPid) ->
    case misc:is_process_alive(PlayerPid) of
        true ->
            case is_integer(Timeout) of
                true -> gen_server:call(PlayerPid, {apply_call, CallType, Moudle, Method, Args}, Timeout);
                false -> gen_server:call(PlayerPid, {apply_call, CallType, Moudle, Method, Args})
            end;
        _ ->
            skip
    end;
apply_call(PlayerId, CallType, Moudle, Method, Args, Timeout) ->
    PlayerPid = misc:get_player_process(PlayerId),
    case misc:is_process_alive(PlayerPid) of
        true ->
            case is_integer(Timeout) of
                true -> gen_server:call(PlayerPid, {apply_call, CallType, Moudle, Method, Args}, Timeout);
                false -> gen_server:call(PlayerPid, {apply_call, CallType, Moudle, Method, Args})
            end;
        _ ->
            skip
    end.

%% 获取主角默认模型列表
get_model_list(Career, Sex, Turn, Lv) ->
    PartList = [?MODEL_PART_HEAD, ?MODEL_PART_CLOTH, ?MODEL_PART_WEAPON],
    [{Part, get_model_id(?LV_MODEL, Part, Career, Sex, Turn, Lv, 0)} || Part <- PartList].
%% 等级模型：
%% 1101（职业+性别(1男2女)+转生次数(未转生之前是00，之后01···)
get_model_id(?LV_MODEL, _Part, _Career, _Sex, _Turn, _Lv, _No) ->
    %% NewLv = 1,
    %% 1*100000000+_Part*1000000+Career*10000+NewLv*100+_No;
    %% Career*1000+Sex*100+Turn;
    transform:get_model_id(_Career, _Turn);
get_model_id(_Model, _Part, _Career, _Sex, _Turn, _Lv, _No) -> 0.

be_hurted(Status, Atter, _AtterSign, _Hurt) ->
    %% @retrun
    lib_dungeon_api:player_be_hurted(Status),
    lib_wasteland_war:player_be_hurted(Status, Atter),
    Status.

%% 玩家死亡处理
%% Status :当前自己的进程状态
%% AtterSign :杀人者类型 1怪, 2人
%% Atter     :杀人者信息 #battle_return_atter{}
%% HitList   :助攻列表 [{[HitId, HitPlatform, HitServerNum], HitTime(毫秒)}|...]
player_die(OldStatus, AtterSign, Atter, _HitList) ->
    #battle_return_atter{id = AttId, mid = MondId, real_name = AttName, lv = AttLv, server_id = AttSvrId} = Atter,
    #player_status{id = RoleId, scene = Scene, scene_pool_id = ScenePId, copy_id = CopyId,
        x = X, y = Y, battle_attr=BA, figure = Figure} = OldStatus,
    #battle_attr{speed = Speed, pk=Pk} = BA,

    %% 伤害其他人
    %% Maps = #{attersign => AtterSign, atter => Atter, hit => HitList},
    %% {ok, Status} = lib_player_event:dispatch(OldStatus, ?EVENT_PLAYER_DIE, Maps),
    %% 修正速度
    lib_scene:change_speed(RoleId, Scene, ScenePId, CopyId, X, Y, Speed, 2),
    %% 对于主城和野外挂机死亡
    NewStatus
    = case data_scene:get(OldStatus#player_status.scene) of
        #ets_scene{type = SceneType} when SceneType == ?SCENE_TYPE_NORMAL orelse
        SceneType == ?SCENE_TYPE_OUTSIDE ->
            %% 红名玩家死亡扣除绑钻
            {Status1, CostBGold}
            = if
                Pk#pk.pk_value =< 0 orelse OldStatus#player_status.bgold =< 0 -> {OldStatus, 0};
                true ->
                    BGold = min(OldStatus#player_status.bgold, 10),
                    {_, Status2} = lib_goods_api:cost_money(OldStatus, ?TYPE_BGOLD, BGold, red_name_be_kill, ""),
                    {Status2, BGold}
            end,

            %% 通知被杀
            {ok, DieBinData} = pt_200:write(20013, [AtterSign, AttName, Pk#pk.pk_value, CostBGold, AttLv, Figure#figure.turn, AttId]),
            lib_server_send:send_to_sid(OldStatus#player_status.sid, DieBinData),

            %% 好友关系修改:暂时去除仇人添加逻辑
            % ?IF(AtterSign == ?BATTLE_SIGN_PLAYER andalso Pk#pk.pk_value == 0 andalso
            %     Pk#pk.pk_status == ?PK_PEACE , lib_relationship:add_enemy(RoleId, AttId), skip),

            %% 触发击杀者成就
            lib_achievement_api:async_event(AttId, lib_achievement_api, kill_player_event, 1),
            Status1;
        _ ->
            {ok, DieBinData} = pt_200:write(20013, [AtterSign, AttName, Pk#pk.pk_value, 0, AttLv, Figure#figure.turn, AttId]),
            lib_server_send:send_to_sid(OldStatus#player_status.sid, DieBinData),
            Node = clusters_node:get_node(),
            ?IF(lib_void_fam:is_in_void_fam(Scene),
                mod_void_fam_local:kill_player(AttId, AttName, RoleId, Figure#figure.name, Scene), skip),
            ?IF(lib_wasteland_war:is_wasteland_scene(Scene),
                lib_wasteland_war:kill_player(Node, Scene, RoleId, OldStatus#player_status.server_id, AttId), skip),
            ?IF(local_boss_api:is_deserted_boss_scene(Scene) andalso AtterSign == ?BATTLE_SIGN_PLAYER, 
                local_boss_api:kill_player(RoleId, Figure#figure.name, AttId, AttName, AttSvrId), skip),
            OldStatus
    end,
    %% Return
    LastBeKill = [{sign, AtterSign}, {id, AttId}, {name, AttName}, {lv, AttLv}, {mid, MondId}],
    NewStatus#player_status{last_be_kill = LastBeKill}.

%% 玩家在跨服场景死亡
%% Status   :当前自己的进程状态
%% AttSign  :杀人者类型 1怪, 2人, 3宠物
%% Atter    :杀人者信息 #battle_return_atter{}
%% HitList  :助攻列表
clusters_player_die(Status, AttSign, Atter, _HitList) ->
    #battle_return_atter{id = AttId, real_name = AttName, lv = AttLv, mid = MondId, server_id = AttSvrId} = Atter,
    #player_status{
        id          = DieId,
        server_id   = _ServerId,
        scene_pool_id = _ScenePId,
        copy_id     = _CopyId,
        scene       = Scene,
        figure      = Figure,
        battle_attr = BA
    } = Status,
    %%钻石大战
    lib_role_drum:player_die(Atter, Status),
    %% 跨服1vn
    lib_kf_1vn:player_die(Status),
    %% 通知被杀
    {ok, DieBinData} = pt_200:write(20013, [AttSign, AttName, BA#battle_attr.pk#pk.pk_value, 0, AttLv, Figure#figure.turn, AttId]),
    lib_server_send:send_to_sid(Status#player_status.sid, DieBinData),
    %% @retrun
    LastBeKill = [{sign, AttSign}, {id, AttId}, {name, AttName}, {lv, AttLv}, {mid, MondId}],
    Node = clusters_node:get_node(),
    ?IF(lib_void_fam:is_in_void_fam(Scene),
        mod_void_fam_local:kill_player(AttId, AttName, DieId, Figure#figure.name, Scene), skip),
    lib_wasteland_war:is_wasteland_scene(Scene) == true andalso
        lib_wasteland_war:kill_player(Node, Scene, DieId, Status#player_status.server_id, AttId),
    ?IF(local_boss_api:is_deserted_boss_scene(Scene) andalso AttSign == ?BATTLE_SIGN_PLAYER, 
        local_boss_api:kill_player(DieId, Figure#figure.name, AttId, AttName, AttSvrId), skip),
    Status#player_status{last_be_kill = LastBeKill}.

%% 更改pk值
%% 更改角色PK状态
%% @param PS #player_status
%% @param Type  ?PK_PEACE | ?PK_FORCE | ?PK_ALL | ?PK_GUILD | ?PK_DIFF_SERVER
%% return->{ok, #player_status{}}
-define(PK_CHANGE_CD, 0).
change_pkstatus(PS, Type) ->
    change_pkstatus(PS, Type, false).
change_pkstatus(PS, Type, IsForce) ->
    #player_status{id = RoleId, copy_id = CopyId, scene = SceneId, scene_pool_id=ScenePoolId,
        x = X, y = Y, sid=Sid
        } = PS,
    case change_pkstatus_do(PS, Type, IsForce) of
        {true, CD, ChangePkPS} ->
            mod_scene_agent:update(ChangePkPS, [{pk, ChangePkPS#player_status.battle_attr#battle_attr.pk}]),
            %% 通知场景的玩家
            {ok, BinData} = pt_120:write(12074, [?BATTLE_SIGN_PLAYER, RoleId, Type]),
            lib_server_send:send_to_area_scene(SceneId, ScenePoolId, CopyId, X, Y, BinData),
            {ok, BinData1} = pt_130:write(13012, [?SUCCESS, Type, CD]),
            lib_server_send:send_to_sid(Sid, BinData1),
            {ok, ChangePkPS};
        {ErrCode, CD} ->
            {ok, BinData} = pt_130:write(13012, [ErrCode, Type, CD]),
            lib_server_send:send_to_sid(Sid, BinData),
            {ok, PS}
    end.

change_pkstatus_do(PS, Type, IsForce) ->
    #player_status{scene = SceneId, change_scene_sign = ChangeSceneSign, battle_attr=BA, figure=Figure
    } = PS,
    #pk{pk_status = OldPkState, pk_change_time=PkChangeTime} = PkStatus = BA#battle_attr.pk,
    NowTime = utime:unixtime(),
    LeftCdTime = 0,
    %% 只有自由切换pk状态的场景才能切换
    IsPkScene = case data_scene:get(SceneId) of
                    #ets_scene{subtype = ?SCENE_SUBTYPE_NORMAL} ->
                        false;
                    _ ->
                        true
                end,
    IsClusterScene = lib_scene:is_clusters_scene(SceneId),
    if
    %% 强制切换，服务端操作
     IsForce ->
         NewPkStatus = PkStatus#pk{pk_status = Type},
         NewPS = PS#player_status{battle_attr = BA#battle_attr{pk=NewPkStatus}},
         {true, LeftCdTime, NewPS};
    %% 不可手动切换为杀戮状态，如需切换请用 IsForce 为true
    Type =:= ?PK_ALL -> {?ERRCODE(err130_no_kill_all), LeftCdTime};
    %% 不能切换为终极和平pk状态
     Type =:= ?PK_PEACE_ULTIMATE -> {?ERRCODE(err130_no_peace_ultimate), LeftCdTime};

    %% 新手不能切换pk状态
     Figure#figure.lv < 2 -> {?ERRCODE(err130_change_pk_lv_lim), LeftCdTime};

    %% 换线中不能切换场景
     ChangeSceneSign == 1 -> {?ERRCODE(err130_change_pk_change_line), LeftCdTime};

    %% 强制pk场景不能手动切换场景
     IsPkScene ->  {?ERRCODE(err130_user_cant_change_pk), LeftCdTime};

    %% 非跨服场景不能切换服务器模式
     Type =:= ?PK_DIFF_SERVER andalso not IsClusterScene ->
         {?ERRCODE(err130_change_pk_not_cluster_scene), LeftCdTime};

    %% 和平切换为其他状态
     OldPkState == 0 andalso Type /= 0 ->
         NewPkStatus = PkStatus#pk{pk_status = Type, pk_change_time=NowTime},
         NewPS = PS#player_status{battle_attr = BA#battle_attr{pk=NewPkStatus}},
         {true, ?PK_CHANGE_CD, NewPS};

    %% 切换为非和平状态 或 已冷却完毕（从非和平切换回和平模式有CD时间）
     Type /= 0 orelse NowTime >= PkChangeTime + ?PK_CHANGE_CD ->
         NewPkStatus = PkStatus#pk{pk_status = Type},
         NewPS = PS#player_status{battle_attr = BA#battle_attr{pk=NewPkStatus}},
         {true, LeftCdTime, NewPS};

     true ->
         {?ERRCODE(err130_change_pk_cd), LeftCdTime}
 end.
get_pk_cd_time(PS) ->
    #player_status{battle_attr=#battle_attr{pk=#pk{pk_change_time=Time}}} = PS,
    Now = utime:unixtime(),
    max(0, Time+?PK_CHANGE_CD-Now).

%% 删除指定位置的快捷栏
delete_quickbar(Local, Q) ->
    case lists:keyfind(Local, 1, Q) of
        false -> Q;
        _ -> lists:keydelete(Local, 1, Q)
    end.

%% 根据类型清理快捷栏
clear_quickbar_by_type(Type, Q, PlayerId) ->
    NewQuickbar = clear_quickbar_by_type_helper(Type, Q, []),
    {ok, BinData} = pt_130:write(13007, [NewQuickbar]),
    lib_server_send:send_to_uid(PlayerId, BinData),
    NewQuickbar.

clear_quickbar_by_type_helper(_Type, [], Result) -> Result;
clear_quickbar_by_type_helper(Type, [{_Local, Type, _Id}|Q], Result) ->
    clear_quickbar_by_type_helper(Type, Q, Result);
clear_quickbar_by_type_helper(Type, [H|Q], Result) ->
    clear_quickbar_by_type_helper(Type, Q, Result++[H]).

%% ----------------------------------------------------------------
%% 世界等级变化
%% ----------------------------------------------------------------
update_world_lv(RolePid, WorldLv) ->
    apply_cast(RolePid, ?APPLY_CAST_STATUS, ?MODULE, role_update_world_lv, [WorldLv]).
role_update_world_lv(Player, _WorldLv) ->
    Player1 = update_exp_add_ratio(Player),
    Player1.

%% 计算所有的经验加成值
%% 更新经验加成: 除了属性id配置，可能还有其他系统
update_exp_add_ratio(Player) ->
    #player_status{
        team = StatusTeam,
        goods_buff_exp_ratio = GoodsBuffExpRatio,
        role_base_attr = {_AttrPower, OriginAttrTotal}
    } = Player,
    %% 属性
    AttrExpRatio = OriginAttrTotal#attr.exp_add_ratio,
    %% 队伍加成
    case StatusTeam of
        #status_team{exp_scale = TeamExpRatio, team_id = TeamId} when TeamId > 0 -> TeamExpRatio;
        _ -> TeamExpRatio = 0
    end,
    %% vip加成
    VipPrivilege = lib_vip_api:get_vip_privilege(Player, ?NBV_GET_MON_EXP_ADD, 1),
    ExpRatio = 1 + (AttrExpRatio + TeamExpRatio)/?RATIO_MAX + GoodsBuffExpRatio + VipPrivilege/100,
    Player#player_status{exp_ratio = ExpRatio}.

%% 获取玩家的掉落概率加成
get_goods_drop_add_ratio(Ps) ->
    %% 物品buff掉落加成
    BuffDropRatio = lib_goods_buff:get_goods_drop_buff(Ps),
    %% 活动掉落概率加成
    BuffDropRatio.

%% 更新放置经验加成系数
-spec update_exp_dun_drop_ratio(#player_status{}) ->
    #player_status{}.
update_exp_dun_drop_ratio(PS) ->
    #player_status{sid = Sid} = PS,
    OldRatio = get_exp_drop_ratio(PS),
    Ratio = calc_exp_drop_add_ratio(PS),
    NewRatio = round(Ratio*10000),
    if OldRatio =/= NewRatio ->
            misc:is_process_alive(Sid) andalso lib_server_send:send_to_sid(Sid, pt_610, 61053, [NewRatio]),
            PS#player_status{exp_dun_drop_ratio = NewRatio};
        true ->
            PS
    end.

%% 获取玩家的放置经验属性系数
-spec get_exp_drop_ratio(#player_status{}) ->
    integer().      %% 万分比分子,使用时要除10000
get_exp_drop_ratio(Player) ->
    case Player#player_status.exp_dun_drop_ratio of
        0 ->
            ?RATIO_MAX;
        _ ->
            Player#player_status.exp_dun_drop_ratio
    end.

%% 计算放置经验挂机总加成系数
-spec calc_exp_drop_add_ratio(Player) -> ReturnType when
    Player :: #player_status{},
    ReturnType :: integer() | float().
calc_exp_drop_add_ratio(Player) when is_record(Player, player_status) ->
    #player_status{battle_attr = BA, figure = #figure{lv = Lv}} = Player,
    %% 世界等级加成
    ServerLv = util:get_world_lv(),
    ServerLvExpRatio = data_exp_add:get_add(ServerLv, Lv),
    %% 放置属性加成
    ExpDropAddRatio = BA#battle_attr.attr#attr.exp_drop_add_ratio,
    %% Vip加成
    VipPrivilege    = lib_vip_api:get_vip_privilege(Player, ?EXP_DUN_VIP_EXP_ADD, 1),
    Ratio           = ?SVR_RATIO_ADD(ExpDropAddRatio, VipPrivilege, ServerLvExpRatio),
    Ratio;
calc_exp_drop_add_ratio(_) ->
    1.

%% @doc 玩家行为互斥加锁
setup_action_lock(RoleId, LockInfo) when is_integer(RoleId) ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, setup_action_lock, [LockInfo]);
setup_action_lock(#player_status{id = RoleId, action_lock = free, dun_multi = DunMulti} = Player, LockInfo) when is_integer(LockInfo) ->
    lib_dun_multi:check_action_lock_in_dun_multi(RoleId, DunMulti, LockInfo),
    Player#player_status{action_lock = LockInfo};

setup_action_lock(#player_status{action_lock = ACLock} = Player, LockInfo) when is_integer(LockInfo) ->
    case ACLock of
        LockInfo ->
            ?ERR("already setup lock [~p], role is ~p ~n", [LockInfo, Player#player_status.id]),
            Player;
        free ->
            Player;
        _OtherInteger ->
            ?ERR("old lock [~p] replace by [~p], role is ~p ~n", [ACLock, LockInfo, Player#player_status.id]),
            Player#player_status{action_lock = LockInfo}
    end.

%% @doc 解开玩家的行为互斥锁
break_action_lock(RoleId, Lock) when is_integer(RoleId)  ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, break_action_lock, [Lock]);
break_action_lock(#player_status{id = RoleId, action_lock = ACLock, dun_multi = DunMulti} = Player, Lock) ->
    case ACLock of
        Lock ->
            lib_dun_multi:check_action_lock_in_dun_multi(RoleId, DunMulti, free),
            Player#player_status{action_lock = free};
        free ->
            ?ERR("already break lock [~p], role is ~p~n", [Lock, Player#player_status.id]),
            Player;
        _OtherInteger ->
            ?ERR("Try to break a exist lock [~p] with [~p], role is ~p~n", [ACLock, Lock, Player#player_status.id]),
            Player
    end.

%% PS|Figure转化为头像信息
trans_to_picture(#player_status{figure = Figure}) ->
    trans_to_picture(Figure);
trans_to_picture(#figure{} = Figure) ->
    #figure{
        id = Id,                                  % 玩家id
        name=Name,                                % 角色昵称
        sex = Sex,                                % 性别 1男 2女
        career = Career,                          % 职业
        vip_type = VipType,                       % Vip类型 1白银 2黄金 3荣耀
        vip = Vip,                                % VIP等级
        turn = Turn,                              % 转生次数（01234）
        lv = Lv,                                  % 等级
        power = Power,                            % 战力
        picture = Picture,                        % 玩家上传的头像地址(默认是玩家id)
        picture_ver = PictureVer,                 % 玩家上传的头像版本号
        profile_photo_id = PhotoId                % 图片id
    } = Figure,
    FrameId = lib_dress_up:get_role_frame_dress(Figure),
    #rec_picture{
        id = Id,                                 % 玩家id
        name=Name,                               % 角色昵称
        sex = Sex,                               % 性别 1男 2女
        career = Career,                         % 职业
        vip_type = VipType,                      % Vip类型 1白银 2黄金 3荣耀
        vip = Vip,                               % VIP等级
        turn = Turn,                             % 转生次数（01234）
        lv = Lv,                                 % 等级
        power = Power,                           % 战力
        picture_frame = FrameId,                 % 头像装扮
        picture = Picture,                       % 玩家上传的头像地址(默认是玩家id)
        picture_ver = PictureVer,                % 玩家上传的头像版本号
        profile_photo_id = PhotoId               % 图片id
    };
trans_to_picture(Unmatch) ->
    ?ERR("trans_to_picture Unmatch:~p~n", [Unmatch]),
    #rec_picture{}.

trans_to_figure(#rec_picture{} = Picture) ->
    #rec_picture{
        id = Id,                                  % 玩家id
        name=Name,                                % 角色昵称
        sex = Sex,                                % 性别 1男 2女
        career = Career,                          % 职业
        vip_type = VipType,                       % Vip类型 1白银 2黄金 3荣耀
        vip = Vip,                                % VIP等级
        turn = Turn,                              % 转生次数（01234）
        lv = Lv,                                  % 等级
        power = Power,                            % 战力
        picture_frame = FrameId,                  % 头像装扮
        picture = PictureId,                      % 玩家上传的头像地址(默认是玩家id)
        picture_ver = PictureVer,                 % 玩家上传的头像版本号
        profile_photo_id = PhotoId                % 图片id
    } = Picture,
    #figure{
        id = Id,                                 % 玩家id
        name=Name,                               % 角色昵称
        sex = Sex,                               % 性别 1男 2女
        career = Career,                         % 职业
        vip_type = VipType,                      % Vip类型 1白银 2黄金 3荣耀
        vip = Vip,                               % VIP等级
        turn = Turn,                             % 转生次数（01234）
        lv = Lv,                                 % 等级
        power = Power,                           % 战力
        dress_list = [{?DRESS_UP_FRAME, FrameId}], % 头像装扮
        picture = PictureId,                     % 玩家上传的头像地址(默认是玩家id)
        picture_ver = PictureVer,                % 玩家上传的头像版本号
        profile_photo_id = PhotoId               % 图片id
    };
trans_to_figure(Unmatch) ->
    ?ERR("trans_to_figure Unmatch:~p~n", [Unmatch]),
    #figure{}.

%% 获得其他玩家的头像信息
get_role_picture(RoleId) ->
    Figure = lib_role:get_role_figure(RoleId),
    trans_to_picture(Figure).

%% 刷新gm身份
%% Player :: integer() | player_status{}
%% return    ok | skip | {ok, #player_status{}}
reload_gm_type(Player) when is_integer(Player) ->
    apply_cast(Player, ?APPLY_CAST_SAVE, lib_player, reload_gm_type, []);
reload_gm_type(Player) when is_record(Player, player_status) ->
    #player_status{id = RoleId, figure = Figure} = Player,
    [GM, _, _, _, _, _, _, _, _, _|_] = get_player_login_data(RoleId),
    NewFigure = Figure#figure{gm = GM},
    NewPlayer = Player#player_status{figure = NewFigure},
    {ok, NewPlayer}.

%% ---------------------------------------------------------------------------
%% @doc cast在线角色进程执行操作
-spec cast_online_player_do(Args) -> ok when
    Args         :: list().     %% [CastType, Moudle, Method, Args] |
%% ---------------------------------------------------------------------------
cast_online_player_do(Args) ->
    OnlinePids = get_online_pids(),
    [
        erlang:apply(lib_player, apply_cast, [Pid | Args]) || Pid <- OnlinePids
    ],
    ok.

%% ---------------------------------------------------------------------------
%% @doc 在线角色进程cast方式执行MFA
-spec cast_online_role_apply(CastType, Moudle, Method, Args) -> Res when
    CastType    :: integer(),           %% Cast类型：?APPLY_CAST|?APPLY_CAST_STATUS|?APPLY_CAST_SAVE
    %% ?APPLY_CAST       ::1 进程cast方式执行MFA
    %% ?APPLY_CAST_STATUS::2 进程cast方式执行MFA，默认添加#player_status{}作为A的第一个参数
    %% ?APPLY_CAST_SAVE  ::3 进程cast方式执行MFA，默认添加#player_status{}作为A的第一个参数，且保存新的#status{}
    Moudle      :: atom(),              %% 模块名
    Method      :: atom(),              %% 方法名
    Args        :: term(),              %% 方法实参
    Res         :: ok.                  %% 结果码
%% ---------------------------------------------------------------------------
cast_online_role_apply(CastType, Moudle, Method, Args) ->
    OnlinePids = get_online_pids(),
    [apply_cast(Pid, CastType, Moudle, Method, Args) || Pid <- OnlinePids],
    ok.

%% 判断是否写入日志
write_log_combat_uplv(PS) ->
    #player_status{figure = Figure} = PS,
    #figure{lv = Lv} = Figure,
    CanWriteCondition1 = lists:member(Lv, ?WRITE_LOG_LVLIST),
    CanWriteCondition2 = (Lv >= 270) and (Lv =< 500) and (Lv rem 10 =:= 0),
    CanWriteCondition3 = (Lv > 500) and (Lv =< 1000) and (Lv rem 20 =:= 0),
    if
        %% 判断当前等级是否为需要记录日志的等级
        CanWriteCondition1 orelse CanWriteCondition2 orelse CanWriteCondition3 ->
            do_write_log_combat_uplv(PS);
        true -> skip
    end.

%% 将升级后玩家的战力构成写入日志
do_write_log_combat_uplv(PS) ->
    #player_status{
        id = Id,
        figure = Figure,
        combat_power = CombatPower
    } = PS,
    #figure{lv = Lv, vip = Vip, career= Career, turn = Turn} = Figure,
    % 获取角色职业名称
    CareerName = data_transform:get_career_transform_name(Career, Turn),

    % 将所有要计算的属性列表按格式放到一个列表中
    AttrList = [],
    Time = utime:unixtime(),
    F = fun({ModuleId, Power}, TmpList) ->
            % 写入日志
            if
                Power =/= 0 ->
                    [[Id, Lv, Vip, CareerName, CombatPower, ModuleId, Power, Time] | TmpList];
                true ->
                    TmpList
            end
    end,
    LogList = lists:foldl(F, [], AttrList),
    lib_log_api:log_combat_uplv(LogList).

%% 功能操作战力日志
log_operation_combat_power(PlayerStatus, NewPlayerStatus, ModId, OperationType, Remark) ->
    OldCombatPower = PlayerStatus#player_status.combat_power,
    NewCombatPower = NewPlayerStatus#player_status.combat_power,
    if
        OldCombatPower =< NewCombatPower -> ok;
        true ->
            Tips = case Remark  of
                       {Module, Fun}  ->
                            lists:concat(["tips: ", Module, ": ", Fun]);
                        _ ->
                            Remark
                    end,
            #player_status{id = RoleId, figure = #figure{name = Name}} = PlayerStatus,
            lib_log_api:log_normal_combat_power(RoleId, Name, ModId, OperationType, OldCombatPower, NewCombatPower, NewCombatPower-OldCombatPower,Tips)
    end.

apply_cast_online(Mod, Func, Arg) ->
    spawn(
        fun() ->
                case ets:first(?ETS_ONLINE) of
                    '$end_of_table' ->
                        ok;
                    Key ->
                        [OnlineRole|_] = ets:lookup(?ETS_ONLINE, Key),
                        apply_cast(OnlineRole#ets_online.pid, ?APPLY_CAST_STATUS, Mod, Func, Arg),
                        apply_cast_online_loop(Key, Mod, Func, Arg)
                end
        end
    ).

apply_cast_online_loop(Key, Mod, Func, Arg) ->
    case ets:next(?ETS_ONLINE, Key) of
        '$end_of_table' ->
            ok;
        NextKey ->
            [OnlineRole|_] = ets:lookup(?ETS_ONLINE, NextKey),
            apply_cast(OnlineRole#ets_online.pid, ?APPLY_CAST_STATUS, Mod, Func, Arg),
            apply_cast_online_loop(NextKey, Mod, Func, Arg)
    end.


%% ---------------------------------------------------------------------------
%% @doc 激活头像
-spec activate_profile_photo(Role, PhotoId) -> {ok, NewRole} | {false, Reason} when
    Role        :: #player_status{},
    PhotoId     :: integer(),
    NewRole     :: #player_status{},
    Reason      :: integer().
%% ---------------------------------------------------------------------------
activate_profile_photo(Role, PhotoId) ->
    #player_status{profile_photo_info = PhotoInfo, id = RoleId,
        figure = Figure} = Role,
    #profile_photo_info{photo_list = PhotoList} = PhotoInfo,
    PhotoCfg = data_profile_photo:get_profile_photo_cfg(PhotoId),
    IsActivated = lists:member(PhotoId, PhotoList),
    if
        not is_record(PhotoCfg, profile_photo) ->
            {false, ?ERRCODE(err130_no_profile_photo_cfg)};
        IsActivated ->
            {false, ?ERRCODE(err130_is_activated)};
        true ->
            #profile_photo{cost = CostId} = PhotoCfg,
            CostType = activate_profile_photo,
            About = io_lib:format("~w", [PhotoId]),
            case lib_goods_api:cost_object_list_with_check(Role, [{CostId, 1}], CostType, About) of
                {false, Errcode, _RoleError} ->
                    {false, Errcode};
                {true, Role2, _} ->
                    PhotoList2 = [PhotoId | PhotoList],
                    PhotoInfo2 = PhotoInfo#profile_photo_info{photo_list = PhotoList2},
                    PhotoInfo3 = calc_profile_photo_attr(PhotoInfo2),
                    Role3 = Role2#player_status{profile_photo_info = PhotoInfo3},
                    PhotoListBin = util:term_to_string(PhotoList2),
                    Sql = io_lib:format(?REPLACE_PHOTO_LIST, [RoleId, PhotoListBin]),
                    db:execute(Sql),
                    Role4 = lib_player:count_player_attribute(Role3),
                    lib_player:send_attribute_change_notify(Role4, ?NOTIFY_ATTR),
                    {ok, Role5} = lib_player:update_player_local_picture_online(Role4, PhotoId),
                    {ColorList, NotOpenNum} = calc_all_profile_photo(PhotoList2, Figure#figure.career, transform:get_transform_id(Role)),
                    {ok, Role6} = lib_wardrobe_active:active_picture(Role5, ColorList, NotOpenNum, PhotoId),
                    lib_common_rank_api:refresh_rank_by_photo(Role6),
                    {ok, Role6}
            end
    end.

calc_profile_photo_attr(ProfilePhotoInfo) ->
    #profile_photo_info{photo_list = PhotoList} = ProfilePhotoInfo,
    AttrList = calc_profile_photo_attr_do(PhotoList, []),
    ProfilePhotoInfo#profile_photo_info{pp_attr = AttrList}.
%% 角色头像属性
calc_profile_photo_attr_do([], AttrList) ->
    AttrList;
calc_profile_photo_attr_do([PhotoId | T], AttrList) ->
    case data_profile_photo:get_profile_photo_cfg(PhotoId) of
        #profile_photo{attr_list = AttrL} ->
            calc_profile_photo_attr_do(T, AttrL ++ AttrList);
        _ ->
            calc_profile_photo_attr_do(T, AttrList)
    end.

%% 统计所有激活的头像(包括转职的头像)
calc_all_profile_photo(PhotoList, Career, TransFormId) ->
    F0 = fun(TTransFormId, TList0) ->
        case data_transform:get_career_transform_image(Career, TTransFormId) of
            TPhotoId0 when is_integer(TPhotoId0) -> [TPhotoId0|TList0];
            _ -> TList0
        end end,
    TransPhotoList = lists:foldl(F0, [], lists:seq(0, TransFormId)),
    PhotoList1 = lists:usort(PhotoList++TransPhotoList),
    F = fun(PhotoId, {TList, TNum}) ->
        IsOpen = is_satisfy_open(PhotoId),
        case data_goods_type:get(PhotoId) of
            #ets_goods_type{color = Color} ->
                case lists:keyfind({1, Color}, 1, TList) of
                    {_, Num} ->
                        {
                            lists:keystore({1, Color}, 1, TList, {{1, Color}, Num + 1}),
                            case IsOpen of false -> TNum + 1; _ -> TNum end
                        };
                    _ ->
                        {
                            lists:keystore({1, Color}, 1, TList, {{1, Color}, 1}),
                            case IsOpen of false -> TNum + 1; _ -> TNum end
                        }
                end;
            _ -> {TList, TNum}
        end end,
    {ColorList,NotOpenNum} = lists:foldl(F, {[], 0}, PhotoList1),
    {ColorList,NotOpenNum}.

is_satisfy_open(PhotoId) ->
    OpenDay = util:get_open_day(),
    NowTime = utime:unixtime(),
    case data_profile_photo:get_profile_photo_cfg(PhotoId) of
        #profile_photo{open_day = CfgOpenDay, open_time = CfgOpenTime}
            when is_integer(CfgOpenDay), is_integer(CfgOpenTime) ->
            case OpenDay >= CfgOpenDay andalso NowTime >= CfgOpenTime of
                true -> true;
                _ -> false
            end;
        _ -> true
    end.

%% ---------------------------------------------------------------------------
%% @doc 更换头像
-spec change_profile_photo(Role, PhotoId) -> {ok, NewRole} | {false, Reason} when
    Role        :: #player_status{},
    PhotoId     :: integer(),
    NewRole     :: #player_status{},
    Reason      :: integer().
%% ---------------------------------------------------------------------------
change_profile_photo(Role, PhotoId) ->
    #player_status{
        profile_photo_info = PhotoInfo, picture_lim = _PictureLim
        ,figure = Figure
    } = Role,
    #figure{sex = _Sex, career= Career, turn = Turn} = Figure,
    #profile_photo_info{photo_list = PhotoList} = PhotoInfo,
    PhotoCfg = data_profile_photo:get_profile_photo_cfg(PhotoId),
    %% 转生头像也放进列表
    TransFormId = transform:get_transform_id(Role),
    F0 = fun(TTransFormId, TList0) ->
        case data_transform:get_career_transform_image(Career, TTransFormId) of
            TPhotoId0 when is_integer(TPhotoId0) -> [TPhotoId0|TList0];
            _ -> TList0
        end end,
    TransPhotoList = lists:foldl(F0, [], lists:seq(0, TransFormId)),
    TransPhotoList1 = [TPhotoId||TPhotoId<-TransPhotoList,lists:member(TPhotoId, PhotoList) == false],
    PhotoList1 = TransPhotoList1 ++ PhotoList,
    IsActivated = case PhotoCfg of
        #profile_photo{career = TCareer}
            when TCareer == [0] orelse length(TCareer) == 2 -> %% 是道具激活类的头像
            lists:member(PhotoId, PhotoList1);
        _ -> %% 检查是不是转生的头像
            TurnList = data_transform:get_transform_by_image(PhotoId),
            TurnList2 = [
                Turn2 || {Career2, Turn2} <- TurnList, Career =:= Career2
            ],
            case TurnList2 of
                [NeedTurn | _] when Turn >= NeedTurn -> %% 转生已达成
                    true;
                _ ->
                    false
            end
    end,
    if
        not IsActivated ->
            {false, ?ERRCODE(err130_is_not_activated)};
        true ->
            {ok, Role2} = lib_player:update_player_local_picture_online(Role, PhotoId),
            {ok, Role2}
    end.

%% -------------------------------------------------------------------------
%% @doc 头像回收
-spec photo_goods_return(Role, PhotoId, InitErrCode) -> ReturnType when
    Role :: #player_status{},
    PhotoId :: integer(),
    InitErrCode :: integer(),
    ReturnType :: {#player_status{}, integer()}.
photo_goods_return(Role, PhotoId, InitErrCode) ->
    case data_profile_photo:get_profile_photo_cfg(PhotoId) of
        #profile_photo{cost = GoodsId} ->
            Cost = [{GoodsId, 1}],
            case data_goods_price:get_goods_price(GoodsId) of
                #goods_price{price_type = PriceType, price = Price} ->
                    CostType = activate_profile_photo,
                    About = io_lib:format("~w", [PhotoId]),
                    case lib_goods_api:cost_object_list_with_check(Role, Cost, CostType, About) of
                        {true, NewRole, _} ->
                            Title = utext:get(1300001), Content = utext:get(1300002),
                            lib_mail_api:send_sys_mail([Role#player_status.id], ?MOD_PLAYER, Title, Content, [{PriceType, 0, round(Price*1)}]),
                            {NewRole, InitErrCode};
                        _ ->
                            {Role, InitErrCode}
                    end;
                _ ->
                    {Role, InitErrCode}
            end;
        _ ->
            {Role, InitErrCode}
    end.

%% 更新玩家信息
player_update_info(Player, Arg) ->
    lib_server_send:send_to_sid(Player#player_status.sid, pt_130, 13015, [Arg]).

%% ---------------------------------------------------------------------------
%% @doc 转生激活并替换头像
-spec activate_turn_photo(Role, PhotoId) -> {ok, NewRole} | {false, Reason} when
    Role        :: #player_status{},
    PhotoId     :: integer(),
    NewRole     :: #player_status{},
    Reason      :: integer().
%% ---------------------------------------------------------------------------
activate_turn_photo(Role, PhotoId) ->
    #player_status{profile_photo_info = PhotoInfo, id = RoleId} = Role,
    #profile_photo_info{photo_list = PhotoList} = PhotoInfo,
    PhotoCfg = data_profile_photo:get_profile_photo_cfg(PhotoId),
    IsActivated = lists:member(PhotoId, PhotoList),
    if
        not is_record(PhotoCfg, profile_photo) ->
            {false, ?ERRCODE(err130_no_profile_photo_cfg)};
        IsActivated ->
            {false, ?ERRCODE(err130_is_activated)};
        true ->
            PhotoList2 = [PhotoId | PhotoList],
            PhotoInfo2 = PhotoInfo#profile_photo_info{photo_list = PhotoList2},
            PhotoInfo3 = calc_profile_photo_attr(PhotoInfo2),
            Role3 = Role#player_status{profile_photo_info = PhotoInfo3},
            PhotoListBin = util:term_to_string(PhotoList2),
            Sql = io_lib:format(?REPLACE_PHOTO_LIST, [RoleId, PhotoListBin]),
            db:execute(Sql),
            Role4 = lib_player:count_player_attribute(Role3),
            lib_player:send_attribute_change_notify(Role4, ?NOTIFY_ATTR),
            {ok, Role5} = lib_player:update_player_local_picture_online(Role4, PhotoId),
            {ok, Role5}
    end.

%% 判断是否需要记录玩家行为
check_log_behavior(Player, Cmd) ->
    case data_protocol:get_module_protocol(Cmd) of
        0 ->
            Player;
        _ ->
            Player#player_status{last_behavior = Cmd}
    end.

%% ------------------------------------------------------------------------------
%% 记录在线跨天日志记前一天的在线时间
-spec set_online_time_log(RoleId :: integer()) -> ok.
set_online_time_log(RoleId) ->
    Now = utime:unixtime(),
    AfterTime = urand:rand(300, 600),
    EndTime = round(Now + AfterTime),
    DefaultEtsId = 1,
    lib_player_record:role_func_check_update(RoleId, ?FUNC_TYPE_MIDNIGHT_LOG, {DefaultEtsId, EndTime}),
    ok.

%% ------------------------------------------------------------------------------
%% 触发在线跨天日志记录
-spec trigger_midnight_online_time_log(Status :: #player_status{}) -> #player_status{}.
trigger_midnight_online_time_log(Status) ->
    #player_status{id = RoleId, last_login_time = LastLoginTime} = Status,
    case utime:is_today(LastLoginTime) of
        true ->
            skip;
        _ -> %% 在线跨天了
            Now = utime:unixtime(),
            %% 今日零时
            Date = utime:unixtime({today, Now}),
            %% 昨日截止时间戳
            LastDayEnd = Date - 1,
            %% 昨日零时
            LastDate = utime:unixtime({today, LastDayEnd}),
            %% 昨日在线时长
            if LastLoginTime > LastDate ->
                    OnlineTime = LastDayEnd - LastLoginTime;
                    % LastLoginTime1 = LastLoginTime;
                true ->
                    OnlineTime = LastDayEnd - LastDate
                    % LastLoginTime1 = LastDate
            end,
            if
                OnlineTime < 60 -> skip;
                true ->
                    SQL = <<"insert into `log_all_online`(player_id, online_time, log_time, logout_time, last_login_time, day)
                    values (~p, ~p, ~p, ~p, ~p, ~p)">>,
                    db:execute(SQL, [RoleId, OnlineTime, LastDayEnd, 0, LastLoginTime, LastDate])
            end,
            ok
    end,
    Status.

%% ----------------------------------------------------------------
%% 查看其他玩家头像信息
%% ----------------------------------------------------------------
send_picture_to_uid(Player,ModId, ToId) ->
    #player_status{figure = Figure} = Player,
    {ok, BinData} = pt_130:write(13017, [ModId, Figure]),
    lib_server_send:send_to_uid(ToId, BinData).

send_picture_to_uid(Player,ModId,Node, ToId) ->
    #player_status{figure = Figure} = Player,
    {ok, BinData} = pt_130:write(13017, [ModId, Figure]),
    clusters_node:apply_cast(lib_server_send, send_to_uid, [Node, ToId, BinData]).

get_picture_from_other_server(ServerId, RoleId, ModId, Node, ToId) ->
    clusters_center:apply_cast(ServerId, ?MODULE, apply_cast, [RoleId, ?APPLY_CAST_STATUS, ?HAND_OFFLINE, ?MODULE, send_picture_to_uid, [ModId, Node, ToId]]).


send_figure_to_uid(Player, ServerId, ModId, ToId) ->
    #player_status{id = Id, figure = Figure, combat_power = Power, server_name = ServerName} = Player,
    {ok, BinData} = pt_130:write(13013, [ServerId, Id, ModId, Power, Figure, ServerName]),
    lib_server_send:send_to_uid(ToId, BinData).

send_figure_to_uid(Player, ServerId, ModId, Node, ToId) ->
    %#ets_role_show{combat_power = Power, figure = Figure} = lib_role:get_role_show(RoleId),
    #player_status{id = Id, figure = Figure, combat_power = Power, server_name = ServerName} = Player,
    {ok, BinData} = pt_130:write(13013, [ServerId, Id, ModId, Power, Figure, ServerName]),
    clusters_node:apply_cast(lib_server_send, send_to_uid, [Node, ToId, BinData]).

get_figure_from_other_server(ServerId, RoleId, ModId, Node, ToId) ->
    clusters_center:apply_cast(ServerId, ?MODULE, apply_cast, [RoleId, ?APPLY_CAST_STATUS, ?HAND_OFFLINE, ?MODULE, send_figure_to_uid, [ServerId, ModId, Node, ToId]]).

%% ----------------------------------------------------------------
%% 查看其他玩家详细信息
%% ----------------------------------------------------------------
%% 查看其他玩家信息-本服
send_role_info_local(Player, ToRole, AskerModulesPower) ->
    BinData = get_role_info(Player, AskerModulesPower),
    lib_server_send:send_to_sid(ToRole, BinData).

%% 其他玩家信息-跨服
send_role_info_kf(Player, ToRole, AskerModulesPower) ->
    BinData = get_role_info(Player, AskerModulesPower),
    clusters_node:apply_cast(lib_server_send, send_to_sid, [ToRole, BinData]),
    Player.

get_role_info(Player, AskerModulePowers) ->
    #player_status{
        figure = Figure,
        off = Off
    } = Player,
    case Off#status_off.goods_status of
        undefined ->
            Online = 1,
            GoodsStatus = lib_goods_do:get_goods_status(); %% 在线
        GoodsStatus ->
            Online = 0, 
            GoodsStatus     %% 离线
    end,
    EquipList = equipment_api:get_other_equipment_list(Player, GoodsStatus),
    EquipListPack = lib_goods_util:pack_equipment_list(EquipList),
    ServerNum = config:get_server_num(),
    WearMate = mate_api:get_wear_mate_role(Player),
    MateName = lib_role:get_role_name(WearMate),
    ModulesPower = get_module_power(Player),
    {MateStage, MateStar} = mate_api:get_mate_goods(Player),
    {ok, BinData} = pt_130:write(13004, [Figure, ServerNum, EquipListPack, ModulesPower, AskerModulePowers, MateName, MateStage, MateStar, Online]),
    BinData.

get_module_power(Player) ->
    %% 1装备 / 2座驾 / 3炫翼 / 4宝具 / 5武器 / 6ai娘 / 7磁炮 / 8星翼 / 9幻甲 / 10宠物 / 11宝宝 / 12灵能
    Modules = [1,2,3,4,5,6,7,8,9,10,11,12],
    lists:foldl(
        fun(Type, Temp) ->
            Power =
                case Type of
                    1 ->
                        equipment_api:get_equipment_power(Player);
                    2 ->
                        lib_role_foster:get_foster_power(Player, ?FHORSE);
                    3 ->
                        lib_role_foster:get_foster_power(Player, ?FWING);
                    4 ->
                        lib_role_foster:get_foster_power(Player, ?FPEARL);
                    5 ->
                        lib_role_foster:get_foster_power(Player, ?FWEAPON);
                    6 ->
                        lib_role_foster:get_foster_power(Player, ?FJARVIS);
                    7 ->
                        lib_role_foster:get_foster_power(Player, ?FGUN);
                    8 ->
                        lib_role_foster:get_foster_power(Player, ?FCLOUD);
                    9 ->
                        lib_role_foster:get_foster_power(Player, ?FARMOUR);
                    10 ->
                        lib_pet:get_pet_power(Player);
                    11 ->
                        lib_child_api:get_baby_power(Player);
                    12 ->
                        lib_role_nucleon:get_nucleon_power(Player)
                end,
            case Power > 0 of
                true  ->
                    [{Type, Power}|Temp];
                false ->
                    Temp
            end
        end, [], Modules
    ).

%% ----------------------------------------------------------------
%% 检查每日可以获得的名望券上限
%% ----------------------------------------------------------------
check_daily_can_get_honour(Player, AddHonour) ->
    #player_status{id = RoleId, guild = StatusGuild} = Player,
    #status_guild{position = Position} = StatusGuild,
    case data_guild:get_guild_pos(Position) of
        #base_guild_pos{reputation_add = DailyAdd} -> skip;
        _ -> DailyAdd = 0
    end,
    %% 宠物生活技能增加每日获得名望券上限
    PetAdd = lib_pet_api:pet_life_skill_about_daily_honour_add(Player),
    TodayNum = lib_daily:get_count(RoleId, ?MOD_GOODS, 2),
    LimitNum = data_key_value:get(1500001),
    AllLimit = LimitNum + DailyAdd + PetAdd,
    LeftNum  = max(AllLimit - TodayNum, 0),
    case (TodayNum+AddHonour) >= AllLimit of
        true when TodayNum >= AllLimit -> false;
        true -> {true, LeftNum};
        _    -> {true, AddHonour}
    end.

get_lv(Player) ->
    case Player#player_status.figure of
        #figure{lv = Lv} ->
            Lv;
        _ ->
            0
    end.
