%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     羁绊逻辑处理
%%% @end
%%%-------------------------------------------------------------------
-module(mate_logic).
-author("liushl").

-include("server.hrl").
-include("mate.hrl").
-include("relationship.hrl").
-include("role.hrl").
-include("figure.hrl").
-include("common.hrl").
-include("predefine.hrl").
-include("def_event.hrl").
-include("def_module.hrl").
-include("flower.hrl").
-include("def_act_label.hrl").
-include("errcode.hrl").
-include("scene.hrl").
-include("def_goods.hrl").
-include("language.hrl").
-include("rec_offline.hrl").
-include("def_fun.hrl").
%% API
-export([
    mate_info/1                         %% 羁绊基本信息
    , ask_mate/2                        %% 请求羁绊
    , check_be_mate/2                   %% 判断能否结成羁绊
    , response_mate_ask/3               %% 回应羁绊请求
    , deal_in_who_ask/3                 %% 在请求方先处理同意
    , relieve_mate/3                    %% 请求解除
    , response_mate_relieve/3           %% 回应请求解除
    , del_in_other/2                    %% 在请求方先处理同意
    , pre_show_intimacy_goods/2         %% 预览跟某个玩家解除羁绊会返还的物品
    , modify_mate_title/3               %% 修改羁绊称号-修改对方
    %, when_my_mate_change_title/3       %% 修改完羁绊称号后的处理
    , modify_mate_remarks/3             %% 修改羁绊备注
    , wear_mate_title/3                 %% 佩戴羁绊称号/取消 1佩戴|2取消
    , get_anniversary/3                 %% 领取纪念日奖励
    , ask_others_mate_info/3            %% 查看其他玩家的羁绊信息
    , get_others_mate_info/3            %% 到对应玩家进程查看羁绊信息
    , enhance_our_boat/1                %% 强化小船 一次升一级
    , mate_act_invite/3                 %% 发出活动邀请
    , mate_act_response/4               %% 回应邀请
    , mate_rhythm_yes_in_who_ask/8      %% 在请求方先处理同意
    , mate_act_get_ready_state/2        %% 准备状态
    , mate_act_get_ready/2              %% 游戏准备
    , mate_act_cancel_invite/1          %% 取消邀请
    , mate_rhythm_tap/3                 %% 节奏大师 点击拍点
    , mate_catch_ball_begin/2           %% 抛接球 开球
    , mate_catch_ball_catch/2           %% 抛接球 击球

    , check_in_other/2                  %% 对方进程判断条件符合

    , hall_release/3                    %% 发布交友信息

    , take_photo_invite/1               %% 拍照- 发出活动邀请
    , take_photo_response/4             %% 拍照- 回应邀请
    , take_photo_ready/4                %% 拍照- 拍照准备

    , refresh_mate_daily/2              %% 羁绊日常- 刷新日常信息
    , receive_task_reward/2             %% 羁绊日常- 领取任务奖励
    , get_task_info/2                   %% 羁绊日常- 获取羁绊日常信息
    , supply_mate_list/2                %% 从战力榜取玩家补足羁绊和路人
    , update_take_progress/2            %% 更新羁绊任务进度
    , check_invite_scene/3

    , get_newbee_reward/1

    , act_label/1
    , gm_end/1
    , gm_end/2
    , gm_stop/1
    , gm_refresh_task/1
]).
%%---------------------------------------------------------------------
%% @doc 羁绊基本信息
%%---------------------------------------------------------------------
mate_info(Player) ->
    #player_status{mate = #role_mate{mate_list = MateList}, id = RoleId} = Player,
    List = lists:foldl(
        fun(#mate_info{
            id = Id,
            title = Title,
            remarks = Remarks,
            time =Time,
            anniversary = Anniversary}, Temp) ->
            case lib_role:get_role_show(Id) of
                #ets_role_show{last_logout_time = LogoutTime, figure = Figure, mate_title = MateTitle} ->
                    case lists:keyfind(RoleId, 1, MateTitle) of
                        {_, OtherTitle} ->
                            OtherTitle;
                        _ ->
                            OtherTitle = data_mate:get_mate_key(default_desg)
                    end,
                    [{Figure, lib_relationship_api:get_intimacy(RoleId, Id), OtherTitle, Remarks,
                        Time, LogoutTime, Title, Anniversary, []}|Temp];
                _ ->
                    Temp
            end
        end, [], MateList
    ),
    List.

%%---------------------------------------------------------------------
%% @doc 结成羁绊伙伴
%%---------------------------------------------------------------------
ask_mate_check(Player, ToRole) ->
    case check_be_mate(Player, ToRole) of
        {false, Err} -> {false, Err};
        true ->
            IsNoResponse = mate_mgr:is_mate_ask_no_response(Player#player_status.id, ToRole),
            CheckCost = lib_goods_api:check_object_list(Player, data_mate:get_mate_key(mate_cost)),
            if
                IsNoResponse =/= false -> {false, ?ERRCODE(err149_exist_no_response)}; %% 双方已经存在申请了，不需要再发一遍
                CheckCost =/= true -> CheckCost;      %% 必须先预备足够的钱
                true -> true
            end
    end.
%% @doc 请求羁绊
ask_mate(Player, TargetRole) ->
    case ask_mate_check(Player, TargetRole) of
        {false, Err} -> {false, Err};
        true ->
            ask_mate_do(Player, TargetRole)
    end.

ask_mate_do(Player, TargetRole) ->
    #player_status{id = RoleId} = Player,
    %% 向管理进程保存信息
    mate_mgr:ask_mate(RoleId, TargetRole),
    %% 给自己发送消息
    mate_mgr:get_ask_mate_info(RoleId),
    %% 给对方发送消息
    mate_mgr:get_ask_mate_info(TargetRole),
    [{_, _, Intimacy}] = lib_relationship_api:get_rela_and_intimacy_online(Player#player_status.id, [TargetRole]),
    ta_agent_fire:mate_friend(Player, [TargetRole, 1, Intimacy]),
    true.


%% 存在请求才能进行回应
response_mate_ask_check(Player, FromRole) ->
    case mate_mgr:is_mate_ask_no_response(Player#player_status.id, FromRole) of
        true ->
            case check_be_mate(Player, FromRole) of
                {false, Err} -> {false, Err};
                true -> true
            end;
        _ ->
            {false, ?ERRCODE(err149_ask_not_exist)}
    end.

%% @doc 回应羁绊请求
response_mate_ask(Player, FromRole, ?MATE_RESPONSE_NO) -> %% 拒绝
    del_ask_msg(Player, FromRole, ?MATE_RESPONSE_NO),
    {true, Player};
response_mate_ask(Player, FromRole, ?MATE_RESPONSE_YES) -> %% 同意
    case response_mate_ask_check(Player, FromRole) of
        {false, Err} ->
            {false, Err};
        true ->
            response_mate_ask_do(Player, FromRole)
    end.

response_mate_ask_do(Player, FromRole) ->
    #player_status{
        id = RoleId,
        figure = #figure{name = Name, sex = Sex}
    } = Player,
    case lib_player:apply_call(FromRole, ?APPLY_CALL_SAVE, ?MODULE, deal_in_who_ask, [RoleId, Sex]) of
        true ->
            #figure{name = MateName, sex = MateSex} = lib_role:get_role_figure(FromRole),
            del_ask_msg(Player, FromRole, ?MATE_RESPONSE_YES),
            Player1 = success_be_mate(Player, FromRole, MateSex, false),

            Reward = data_mate:get_mate_key(mate_rewards),
            lib_mail_api:send_sys_mail([RoleId], utext:get(?LAN_TITLE_MATE_BE_MATE), utext:get(?LAN_CONTENT_MATE_BE_MATE, [MateName]), Reward),
            lib_mail_api:send_sys_mail([FromRole], utext:get(?LAN_TITLE_MATE_BE_MATE), utext:get(?LAN_CONTENT_MATE_BE_MATE, [Name]), Reward),
            lib_chat:send_TV({all}, ?MOD_MATE, 1, [Name, MateName]),
            lib_log_api:log_mate_rela(FromRole, MateName, RoleId, Name, 1, util:term_to_bitstring(data_mate:get_mate_key(mate_cost))),
            [{_, _, Intimacy}] = lib_relationship_api:get_rela_and_intimacy_online(Player#player_status.id, [FromRole]),
            ta_agent_fire:mate_friend(Player, [FromRole, 2, Intimacy]),
            {true, Player1};
        {false, Err} ->
            {false, Err}
    end.
%% 在请求方先处理同意
deal_in_who_ask(Player, TargetRole, TargetSex) ->
    Cost = data_mate:get_mate_key(mate_cost),
    case lib_goods_api:cost_object_list_with_check(Player, Cost, mate_be_mate, "") of
        {true, Player1, _}  ->
            Player2 = success_be_mate(Player1, TargetRole, TargetSex, true),
            {true, Player2};
        {false, _, _} ->
            {{false, ?ERRCODE(err149_other_less_gold)}, Player}
    end.

%% @doc 结成羁绊关系
success_be_mate(Player, TargetRole, TargetSex, NeedSave) ->
    #player_status{id = RoleId, mate = Mate, figure = Figure} = Player,
    #role_mate{mate_list = MateList, wear_mate = WearMate} = Mate,
    Title = data_mate:get_mate_key(default_desg),
    OneMate = #mate_info{
        id = TargetRole,
        remarks = get_default_remarks(Figure#figure.sex, TargetSex),
        title = Title,
        time = utime:unixdate()
    },
    F = fun() ->
        db_save_one_mate(RoleId, OneMate),
        db_save_one_mate(TargetRole, OneMate#mate_info{id = RoleId})
        end,
    NeedSave andalso db:transaction(F),
    NewMateList = lists:keystore(TargetRole, #mate_info.id, MateList, OneMate),
    Mate1 = Mate#role_mate{mate_list = NewMateList},
    Player1 = Player#player_status{mate = Mate1},
    %% 更新缓存
    lib_role:update_role_show(RoleId, [{mate_title, {TargetRole, Title}}]),
    %% 派发羁绊结交事件：其他功能使用事件触发
    {ok, Player3} = lib_player_event:dispatch(Player1, ?EVENT_BE_MATE, TargetRole),
    %% 自动佩戴羁绊称号
    Player5 =
        case WearMate =:= 0 of
            true  ->
                case wear_mate_title(Player3, TargetRole, 1) of
                    {true, Player4} -> Player4;
                    _ -> Player3
                end;
            false ->
                Player3
        end,
    %% 完成任务
    lib_task_api:be_mate(Player5),
    lib_achievement_api:mate_event(Player5, erlang:length(NewMateList)),
    lib_seven_days_goal:finish_task(RoleId, ?MOD_MATE, 1, erlang:length(NewMateList), true),
    %% > 最后计算战力
    PlayerEnd = mate_api:update_mate_power(Player5),
    pp_mate:handle(14900, PlayerEnd, []),
    PlayerEnd.

%% 根据双方不同性别确认默认标签
get_default_remarks(1, 1) ->
    4;      %% 两男为基
get_default_remarks(2, 2) ->
    2;      %% 两女为闺
get_default_remarks(_, _) ->
    3.      %% 其他为死党
%% 删除信息
del_ask_msg(Player, FromRole, Type) ->
    mate_mgr:del_ask_mate(FromRole, Player#player_status.id),
    %% 通知对方
    lib_server_send:send_to_uid(FromRole, pt_149, 14904, [Player#player_status.figure#figure.name, Type]),
    ok.
%%---------------------------------------------------------------------
%% @doc 解除羁绊 1 协议解除羁绊/2 强制解除羁绊
%%---------------------------------------------------------------------
relieve_mate_check(Player, TargetRole, ?MATE_RELIEVE_DEAL) ->
    #player_status{id = RoleId, mate = #role_mate{mate_list = MateList}} = Player,
    AlreadyMate = lists:keymember(TargetRole, #mate_info.id, MateList),
    IsNoResponse = mate_mgr:is_mate_relieve_no_response(RoleId, TargetRole),
    if
        not AlreadyMate -> {false, ?ERRCODE(err149_not_mate)};
        IsNoResponse -> {false, ?ERRCODE(err149_exist_no_response)};
        true -> true
    end;
relieve_mate_check(Player, TargetRole, ?MATE_RELIEVE_FORCE) ->
    #player_status{id = _RoleId, mate = #role_mate{mate_list = MateList}} = Player,
    AlreadyMate = lists:keymember(TargetRole, #mate_info.id, MateList),
    IsOnline = lib_player:is_online_global(TargetRole),
    LogoutTime = lib_role:get_role_logout_time(TargetRole),
    DiffDay = utime:diff_day(LogoutTime),
    RelieveDay = data_mate:get_mate_key(autobreak_day),
    Price = data_mate:get_mate_key(break_cost),
    if
        not AlreadyMate -> {false, ?ERRCODE(err149_not_mate)};
        IsOnline -> {true, Price};
        DiffDay >= RelieveDay -> true;
        true -> {true, Price}
    end.

%% @doc 请求解除
relieve_mate(Player, TargetRole, Type) ->
    case relieve_mate_check(Player, TargetRole, Type) of
        {false, Err} -> {false, Err};
        true -> relieve_mate_do(Player, TargetRole, Type, []);
        {true, Cost} ->
            case lib_goods_api:cost_object_list_with_check(Player, Cost, relieve_mate, "") of
                {true, Player1, _} ->
                    relieve_mate_do(Player1, TargetRole, Type, Cost);
                {false, Err, _} -> {false, Err}
            end
    end.

relieve_mate_do(Player, TargetRole, ?MATE_RELIEVE_DEAL, _) -> %% 协议解除羁绊
    #player_status{id = RoleId} = Player,
    %% 向管理进程保存信息
    mate_mgr:consult_relieve_mate(RoleId, TargetRole),
    %% 给自己发送消息
    mate_mgr:get_relieve_mate_info(RoleId),
    %% 给对方发送消息
    mate_mgr:get_relieve_mate_info(TargetRole),
    {true, Player};
relieve_mate_do(Player, TargetRole, ?MATE_RELIEVE_FORCE, Cost) -> %% 强制解除羁绊
    go_relieving_mate(Player, TargetRole, 2, Cost).

%%---------------------------------------------------------------------
%% 1同意|2拒绝
response_mate_relieve_check(Player, TargetRole) ->
    #player_status{id = RoleId, mate = #role_mate{mate_list = MateList}} = Player,
    AlreadyMate = lists:keymember(TargetRole, #mate_info.id, MateList),
    IsNoResponse = mate_mgr:is_mate_relieve_no_response(RoleId, TargetRole),
    if
        not AlreadyMate -> {false, ?ERRCODE(err149_not_mate)};
        IsNoResponse =/= true -> {false, ?ERRCODE(err149_ask_not_exist)};
        true ->
            case mate_mgr:del_relieve_mate(TargetRole, RoleId) of
                false -> {false, ?ERRCODE(err149_ask_not_exist)};
                true  -> true
            end
    end.

%% @doc 回应请求解除
response_mate_relieve(Player, TargetRole, ?MATE_RESPONSE_NO) ->
    mate_mgr:del_relieve_mate(TargetRole, Player#player_status.id),
    {true, Player};
response_mate_relieve(Player, TargetRole, ?MATE_RESPONSE_YES) ->
    case response_mate_relieve_check(Player, TargetRole) of
        {false, Err} -> {false, Err};
        true ->
            response_mate_relieve_do(Player, TargetRole)
    end.

%% 同意解除
response_mate_relieve_do(Player, TargetRole) ->
    lib_server_send:send_to_uid(TargetRole, pt_149, 14908, [Player#player_status.figure#figure.name, ?MATE_RESPONSE_YES]),
    go_relieving_mate(Player, TargetRole, 3, []).

%% 走解除羁绊的流程: 我强制解除或者对方同意解除
go_relieving_mate(Player, TargetRole, OpType, Cost) ->
    #player_status{
        id = RoleId,
        figure = #figure{name = Name}
    } = Player,
    %% 对方处理
    case del_other_first(RoleId, TargetRole) of
        true ->
            mate_mgr:remove_relieve_mate(RoleId, TargetRole),
            MateName = lib_role:get_role_name(TargetRole),
            %% 日志
            lib_log_api:log_mate_rela(RoleId, Name, TargetRole, MateName, OpType, util:term_to_bitstring(Cost)),
            
            %% 邮件通知
            Player1 = success_relieve_mate(Player, TargetRole, true),
            {Intimacy, Contribute} = lib_relationship:get_intimacy_contribute_with_other(RoleId, TargetRole),
            Type = case OpType == 2 of
                true  -> 3;
                false -> 4
            end,
            ta_agent_fire:mate_friend(Player, [TargetRole, Type, Intimacy]),
            
            %% 扣除亲密度
            lib_relationship:update_intimacy_each_one(RoleId, TargetRole, -Intimacy, ?INTIMACY_TYPE_CLEAN, []),

            SelfGoods = calc_intimacy_goods(Intimacy, Contribute),
            lib_mail_api:send_sys_mail([RoleId], utext:get(?LAN_TITLE_MATE_CANCEL_MATE), utext:get(?LAN_CONTENT_MATE_CANCEL_MATE, [MateName]), SelfGoods),

            TargetGoods = calc_intimacy_goods(Intimacy, Intimacy - Contribute),
            lib_mail_api:send_sys_mail([TargetRole], utext:get(?LAN_TITLE_MATE_CANCEL_MATE), utext:get(?LAN_CONTENT_MATE_CANCEL_MATE, [Name]), TargetGoods),

            lib_chat:send_TV({all}, ?MOD_MATE, 2, [Name, MateName]),
            {true, Player1};
        {false, Err} ->
            {false, Err}
    end.

del_other_first(RoleId, TargetRole) ->
    case catch lib_player:apply_call(TargetRole, ?APPLY_CAST_SAVE, ?MODULE, del_in_other, [RoleId]) of
        skip ->
            db:execute(?SQL_MATE_UPDATE_WEAR_MATE, [0, TargetRole, RoleId]),
            offline_relieve_msg(TargetRole, RoleId, false);
        true ->
            true;
        _ ->
            {false, 0}
    end.
del_in_other(Player, TargetRole) ->
    {true, success_relieve_mate(Player, TargetRole, false)}.

%% @doc 成功解除羁绊关系
success_relieve_mate(Player, TargetRole, NeedDel) ->
    #player_status{
        id = RoleId,
        mate = Mate
    } = Player,
    #role_mate{mate_list = MateList, wear_mate = WearMate} = Mate,
    offline_relieve_msg(RoleId, TargetRole, NeedDel),
    NewMateList = lists:keydelete(TargetRole, #mate_info.id, MateList),
    Mate1 = Mate#role_mate{mate_list = NewMateList},
    Player1 = Player#player_status{mate = Mate1},
    %% 派发羁绊解除事件：其他功能使用事件触发
    {ok, Player3} = lib_player_event:dispatch(Player1, ?EVENT_RELIEVE_MATE, TargetRole),
    %% 自动撤销羁绊称号
    Player5 =
        case WearMate =:= TargetRole of
            true  ->
                case wear_mate_title(Player3, TargetRole, 2) of
                    {true, Player4} -> Player4;
                    _ -> Player3
                end;
            false ->
                Player3
        end,
    %% > 最后计算战力
    PlayerEnd = mate_api:update_mate_power(Player5),
    lib_achievement_api:mate_event(Player, erlang:length(NewMateList)),
    lib_seven_days_goal:finish_task(RoleId, ?MOD_MATE, 1, erlang:length(NewMateList), true),
    pp_mate:handle(14900, PlayerEnd, []),
    PlayerEnd.

%% 离线玩家必须要做的事
offline_relieve_msg(RoleId, TargetRole, NeedDel) ->
    F = fun() ->
        db_del_mate(RoleId, TargetRole),
        db_del_mate(TargetRole, RoleId)
        end,
    NeedDel andalso db:transaction(F),
    %% 更新缓存
    lib_role:update_role_show(RoleId, [{mate_title, {TargetRole, ""}}]),
    true.

%% 预览跟某个玩家解除羁绊会返还的物品
pre_show_intimacy_goods(Player, RoleId) ->
    {Intimacy, Contribute} = lib_relationship:get_intimacy_contribute_with_other(Player#player_status.id, RoleId),
    calc_intimacy_goods(Intimacy, Contribute).
calc_intimacy_goods(_, 0) -> [];
calc_intimacy_goods(0, _Contribute) -> [];
calc_intimacy_goods(Intimacy, Contribute) ->
    Percent = data_mate:get_mate_key(break_return) / 10000,
    SelfIntimacy = round(Intimacy * Percent * (Contribute / Intimacy)),
    SelfGoods = trans_intimacy_to_flower(SelfIntimacy),
    SelfGoods.

trans_intimacy_to_flower(Intimacy) ->
    trans_intimacy_to_flower(Intimacy, lists:reverse(data_flower:get_flower_gift_cfg_ids()), []).
trans_intimacy_to_flower(Intimacy, _, GoodsList) when Intimacy =< 0 -> GoodsList;
trans_intimacy_to_flower(Intimacy, [GoodsId|T], GoodsList) ->
    #flower_gift_cfg{intimacy = GoodsIntimacy} = data_flower:get_flower_gift_cfg(GoodsId),
    case Intimacy div GoodsIntimacy of
        N when N > 0 ->
            trans_intimacy_to_flower(Intimacy-GoodsIntimacy*N, T, [{?TYPE_GOODS, GoodsId, N}|GoodsList]);
        _ ->
            trans_intimacy_to_flower(Intimacy, T, GoodsList)
    end.
%%---------------------------------------------------------------------
%% @doc 修改羁绊称号-修改对方
%% 一开始策划是要求修改对方的头顶称号，现在又要求修改自己头顶称号，防止策划又改回来，源代码就不删了
%%modify_mate_title(Player, TargetRole, Title) ->
%%    #player_status{mate = #role_mate{mate_list = MateList}, id = RoleId} = Player,
%%    OneMate = lists:keyfind(TargetRole, #mate_info.id, MateList),
%%    CheckTitle = lib_word:validate_name([width, keyword], Title),
%%    if
%%        OneMate =:= false -> {false, ?ERRCODE(err149_not_mate)};
%%        CheckTitle == {false, width} -> {false, ?ERRCODE(not_enough_length)};
%%        CheckTitle == {false, invalid_utf8} -> {false, ?ERRCODE(illegal_character)};
%%        CheckTitle == {false, keyword} -> {false, ?ERRCODE(err145_2_word_is_sensitive)};
%%        true ->
%%            db:execute(?SQL_MATE_UPDATE_MATE_TITLE, [Title, TargetRole, RoleId]),
%%            lib_role:update_role_show(TargetRole, [{mate_title, {RoleId, Title}}]),
%%            lib_player:apply_cast(TargetRole, ?APPLY_CAST_STATUS, ?MODULE, when_my_mate_change_title, [RoleId, Title]),
%%            {true, Player}
%%    end.
%%when_my_mate_change_title(Player, MateRole, Title) ->
%%    #player_status{mate = #role_mate{mate_list = MateList, wear_mate = WearMate} = RoleMate} = Player,
%%    case lists:keyfind(MateRole, #mate_info.id, MateList) of
%%        #mate_info{} = OneMate ->
%%            NewMateRole = OneMate#mate_info{title = Title},
%%            NewMateList = lists:keystore(MateRole, #mate_info.id, MateList, NewMateRole),
%%            case WearMate =:= MateRole of
%%                true  ->
%%                    mod_scene_agent:update(Player, [{mate_title, Title}]),
%%                    lib_scene:broadcast_player_scene_info(Player, [{?SCENE_USER_UPDATE_MATE_TITLE, Title}]);
%%                false ->
%%                    ok
%%            end,
%%            Player#player_status{mate = RoleMate#role_mate{mate_list = NewMateList}};
%%        false ->
%%            Player
%%    end.
%% 修改自己头顶称号：某某某的XXX
modify_mate_title(Player, TargetRole, Title) ->
    #player_status{
        mate = #role_mate{mate_list = MateList, wear_mate = WearMate} = RoleMate,
        id = RoleId
    } = Player,
    OneMate = lists:keyfind(TargetRole, #mate_info.id, MateList),
    CheckTitle = lib_word:validate_name([width, keyword], Title),
    if
        OneMate =:= false -> {false, ?ERRCODE(err149_not_mate)};
        CheckTitle == {false, width} -> {false, ?ERRCODE(not_enough_length)};
        CheckTitle == {false, invalid_utf8} -> {false, ?ERRCODE(illegal_character)};
        CheckTitle == {false, keyword} -> {false, ?ERRCODE(err145_2_word_is_sensitive)};
        CheckTitle =/= true -> {false, 0};
        true ->
            NewMateRole = OneMate#mate_info{title = Title},
            NewMateList = lists:keystore(TargetRole, #mate_info.id, MateList, NewMateRole),
            db:execute(?SQL_MATE_UPDATE_MATE_TITLE, [Title, RoleId, TargetRole]),
            lib_role:update_role_show(RoleId, [{mate_title, {TargetRole, Title}}]),
            case WearMate =:= TargetRole of
                true  ->
                    mod_scene_agent:update(Player, [{mate_title, Title}]),
                    lib_scene:broadcast_player_scene_info(Player, [{?SCENE_USER_UPDATE_MATE_TITLE, Title}]);
                false ->
                    ok
            end,
            Player1 = Player#player_status{mate = RoleMate#role_mate{mate_list = NewMateList}},
            {true, Player1}
    end.

%%---------------------------------------------------------------------
%% @doc 修改羁绊备注
modify_mate_remarks(Player, TargetRole, Remarks) ->
    #player_status{mate = #role_mate{mate_list = MateList} = RoleMate, id = RoleId} = Player,
    OneMate = lists:keyfind(TargetRole, #mate_info.id, MateList),
    if
        OneMate =:= false -> {false, ?ERRCODE(err149_not_mate)};
        true ->
            NewMateRole = OneMate#mate_info{remarks = Remarks},
            db_save_one_mate(RoleId, NewMateRole),
            NewMateList = lists:keystore(TargetRole, #mate_info.id, MateList, NewMateRole),
            {true, Player#player_status{mate = RoleMate#role_mate{mate_list = NewMateList}}}
    end.
%%---------------------------------------------------------------------
%% @doc 佩戴羁绊称号/取消 1佩戴|2取消
wear_mate_title(Player, MateRole, Type) ->
    #player_status{mate = RoleMate, id = RoleId} = Player,
    #role_mate{mate_list = MateList, wear_mate = WearMate} = RoleMate,
    OneMate = lists:keyfind(MateRole, #mate_info.id, MateList),
    if
        Type =:= 1 andalso OneMate =:= false -> {false, ?ERRCODE(err149_not_mate)};
        Type =:= 1 andalso MateRole =:= WearMate -> {false, ?ERRCODE(err149_already_wear_title)};
        Type =:= 2 andalso MateRole =:= 0 -> {false, ?ERRCODE(err149_already_unwear_title)};
        true ->
            case Type of
                1 -> %% 佩戴
                    MateName = lib_role:get_role_name(MateRole),
                    MateTitle = OneMate#mate_info.title,
                    NewWearMate = MateRole;
                _ ->
                    MateName = "",
                    MateTitle = "",
                    NewWearMate = 0
            end,
            NewRoleMate = RoleMate#role_mate{wear_mate = NewWearMate},
            db_save_role_mate(RoleId, NewRoleMate),
            NewPlayer = Player#player_status{mate = NewRoleMate},
            lib_player:player_update_info(NewPlayer, [{1, NewWearMate}]),
            mod_scene_agent:update(NewPlayer, [{mate_name, MateName},{mate_title, MateTitle}]),
            lib_scene:broadcast_player_scene_info(NewPlayer, [{?SCENE_USER_UPDATE_MATE_ROLE, MateName},{?SCENE_USER_UPDATE_MATE_TITLE, MateTitle}]),
            {true, NewPlayer}
    end.
%%---------------------------------------------------------------------
%% @doc 领取纪念日奖励
get_anniversary(Player, TargetRole, Day) ->
    #player_status{mate = #role_mate{mate_list = MateList} = RoleMate, id = RoleId, figure = Figure} = Player,
    OneMate = lists:keyfind(TargetRole, #mate_info.id, MateList),
    Config = data_mate:get_mate_anniversary_day(Day),
    if
        OneMate =:= false -> {false, ?ERRCODE(err149_not_mate)};
        Config =:= [] -> {false, ?ERRCODE(err149_no_award)};
        true ->
            #mate_info{anniversary = Anniversary, time = Time} = OneMate,
            Now = utime:unixtime(),
            DiffDay = utime:day_span(Now, Time),
            AlreadyGet = lists:member(Day, Anniversary),
            if
                Day > DiffDay -> {false, ?ERRCODE(err149_less_day)};
                AlreadyGet -> {false, ?ERRCODE(err149_already_get_award)};
                true ->
                    NewOneMate = OneMate#mate_info{anniversary = [Day|Anniversary]},
                    db_save_one_mate(RoleId, NewOneMate),
                    NewMateList = lists:keystore(TargetRole, #mate_info.id, MateList, NewOneMate),
                    Player1 = lib_goods_api:send_reward(Player#player_status{mate = RoleMate#role_mate{mate_list = NewMateList}}, Config, mate_anniversary),
                    lib_log_api:log_mate_anniversary(
                        RoleId, Figure#figure.name, TargetRole, lib_role:get_role_name(TargetRole),
                        util:term_to_bitstring(Config), Day, DiffDay, Now
                        ),
                    {true, Player1}
            end
    end.
%%---------------------------------------------------------------------
%% @doc 查看其他玩家的羁绊信息
ask_others_mate_info(Sid, RoleId, MateId) ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?HAND_OFFLINE, ?MODULE, get_others_mate_info, [MateId, Sid]).
get_others_mate_info(Player, MateId, SendSid) ->
    #player_status{
        mate = #role_mate{mate_list = MateList},
        id = RoleId,
        figure = Figure,
        off = OffData
    } = Player,
    case lists:keyfind(MateId, #mate_info.id, MateList) of
        #mate_info{
            title = Title,
            remarks = Remarks,
            time =Time
        } ->
            case lib_role:get_role_show(MateId) of
                #ets_role_show{figure = MateFigure, mate_title = MateTitle} ->
                    case lists:keyfind(RoleId, 1, MateTitle) of
                        {_, OtherTitle} ->
                            OtherTitle;
                        _ ->
                            OtherTitle = data_mate:get_mate_key(default_desg)
                    end,
                    case OffData#status_off.relationship of
                        undefined ->
                            Relationship = lib_relationship:get_rela_on_dict(RoleId);
                        Relationship ->
                            ok
                    end,
                    Intimacy =
                        case lists:keyfind(MateId, #rela.other_rid, Relationship) of
                            #rela{intimacy = _Intimacy} ->
                                _Intimacy;
                            _ ->
                                0
                        end,
                    {ok, Bin} = pt_149:write(14916, [
                        Intimacy,
                        Time,
                        Remarks,
                        [{Title, Figure}, {OtherTitle, MateFigure}]
                    ]),
                    lib_server_send:send_to_sid(SendSid, Bin);
                _ ->
                    lib_chat:send_error_code(SendSid, ?ERRCODE(err149_mate_info_overdue))
            end;
        _ ->
            lib_chat:send_error_code(SendSid, ?ERRCODE(err149_mate_info_overdue))
    end,
    Player.

%%---------------------------------------------------------------------
%% @doc 加强友谊的小船
%%---------------------------------------------------------------------
enhance_our_boat_check(Player) ->
    #player_status{mate = #role_mate{
        boat_stage = BoatLv,
        boat_star = BoatStar,
        boat_exp = BoatExp
    }} = Player,
    NextLvConfig = get_boat_next_lv(BoatLv, BoatStar),
    #base_mate_boat{exp = Exp} = data_mate:get_mate_boat(BoatLv, BoatStar),
    LvNeed = lib_module:get_open_lv(?MOD_MATE),
    GoodsId = data_mate:get_mate_key(ship_item),
    [{_, HasNum}] = lib_goods_api:get_goods_num(Player, [GoodsId]),
    if
        Player#player_status.figure#figure.lv < LvNeed -> {false, ?ERRCODE(err149_less_lv)};
        NextLvConfig =:= [] -> {false, ?ERRCODE(err149_boat_max_lv)};
        HasNum =:= 0 -> {false, ?GOODS_NOT_ENOUGH};
        true  ->
            NeedExp = max(0, Exp-BoatExp),
            GoodsExp = data_mate:get_mate_key(ship_item_exp),
            NeedGoodsNum = umath:ceil(NeedExp / GoodsExp),
            FinalNum = min(HasNum, NeedGoodsNum),
            {true, GoodsExp*FinalNum, [{?TYPE_GOODS, GoodsId, FinalNum}]}
    end.
get_boat_next_lv(BoatLv, BoatStar) ->
    case data_mate:get_mate_boat(BoatLv, BoatStar+1) of
        [] -> data_mate:get_mate_boat(BoatLv+1, 1);
        Config -> Config
    end.
%% 强化小船 一次升一级
enhance_our_boat(Player) ->
    case enhance_our_boat_check(Player) of
        {false, Err} -> {false, Err};
        {true, AddExp, CostGoods} ->
            case lib_goods_api:cost_object_list_with_check(Player, CostGoods, mate_enhance_our_boat, "") of
                {false, Err, _} -> {false, Err};
                {true, Player1, _} ->
                    enhance_our_boat_do(Player1, AddExp, CostGoods)
            end
    end.
enhance_our_boat_do(Player, AddExp, Cost) ->
    #player_status{mate = #role_mate{boat_exp = BoatExp} = RoleMate, id = RoleId, figure = Figure} = Player,
    {NewRoleMate, IsUp} = calc_lv_up(RoleMate#role_mate{boat_exp = BoatExp + AddExp}, false),
    db_save_role_mate(RoleId, NewRoleMate),
    Player1 = Player#player_status{mate = NewRoleMate},
    lib_log_api:log_mate_boat(
        RoleId, Figure#figure.name, NewRoleMate#role_mate.boat_stage, NewRoleMate#role_mate.boat_star, NewRoleMate#role_mate.boat_exp,
        util:term_to_bitstring(Cost), utime:unixtime()
    ),
    case IsUp of
        true  ->
            %% > 最后计算战力
            PlayerEnd = mate_api:update_mate_power(Player1),
            {true, PlayerEnd};
        false ->
            {true, Player1}
    end.

calc_lv_up(RoleMate, Bool) ->
    #role_mate{
        boat_stage = BoatLv,
        boat_star = BoatStar,
        boat_exp = BoatExp
    } = RoleMate,
    case data_mate:get_mate_boat(BoatLv, BoatStar) of
        #base_mate_boat{exp = Exp} when BoatExp >= Exp ->
            case get_boat_next_lv(BoatLv, BoatStar) of
                #base_mate_boat{stage = NextLv, star = NextStar} ->
                    calc_lv_up(RoleMate#role_mate{boat_stage = NextLv, boat_star = NextStar, boat_exp = BoatExp-Exp}, true);
                _ ->
                    {RoleMate, true}
            end;
        _ ->
            {RoleMate, Bool}
    end.
%%---------------------------------------------------------------------
%% @doc 小游戏-玩耍一下
%%---------------------------------------------------------------------
check_play_mate_act(Player, TargetRole, ActId) ->
    #player_status{mate = #role_mate{mate_list = MateList}, act_label = ActLabel, action_lock = ActionLock} = Player,
    IsMate = lists:keymember(TargetRole, #mate_info.id, MateList),
    ActOpen = mate_mgr:is_mate_act_open(ActId),
    TargetOnline = lib_player:is_online_global(TargetRole),
    if
        not IsMate -> {false, ?ERRCODE(err149_not_mate)};
        ActOpen =:= 0 -> {false, ?ERRCODE(err149_act_not_open)};
        not TargetOnline -> {false, ?ERRCODE(err149_target_offline)};
        ActionLock =/= free -> {false, ActionLock, busy};
        ActLabel =/= [] -> {false, ?ERRCODE(err149_already_in_act), busy};
        true -> true
    end.

%% @doc 发出活动邀请
mate_act_invite(Player, TargetRole, ActId) ->
    case check_play_mate_act(Player, TargetRole, ActId) of
        {false, Err, _} -> {false, Err};
        {false, Err} -> {false, Err};
        true ->
            #player_status{id = RoleId} = Player,
            mate_mgr:mate_act_invite(RoleId, TargetRole, ActId),
            mate_mgr:get_mate_act_invite_info(TargetRole),
            mate_mgr:get_mate_act_invite_info(RoleId),
            true
    end.

%% @doc 回应邀请
mate_act_response(Player, FromRole, ActId, ?MATE_RESPONSE_NO) ->
    mate_mgr:mate_act_response(FromRole, Player#player_status.id, ActId, ?MATE_RESPONSE_NO, []),
    true;
mate_act_response(Player, FromRole, ActId, ?MATE_RESPONSE_YES) ->
    case check_play_mate_act(Player, FromRole, ActId) of
        {false, Err, _} -> {false, Err};
        {false, Err} ->
            {false, Err};
        true ->
            #player_status{id = RoleId, scene = Scene, scene_pool_id = ScenePoolId, x = X, y = Y} = Player,
            case mate_mgr:is_mate_act_no_response_check_invite(FromRole, RoleId, ActId) of
                true  ->
                    %% 以防对方处于不可玩的状态
                    DailyScore = lib_daily:get_count(RoleId, ?MOD_MATE, ActId),
                    lib_player:apply_cast(FromRole, ?APPLY_CAST_STATUS, ?MODULE, mate_rhythm_yes_in_who_ask, [RoleId, ActId, [{RoleId, DailyScore}], Scene, ScenePoolId, X, Y]),
                    true;
                {false, Err} ->
                    {false, Err}
            end
    end.
%% 同意的时候先去发起方处理
mate_rhythm_yes_in_who_ask(Player, TargetRole, ActId, DailyScoreList, _TargetRoleScene, _TargetRoleScenePoolId, _TargetRoleX, _TargetRoleY) ->
    case check_play_mate_act(Player, TargetRole, ActId) of
        {false, _, _} ->
            lib_server_send:send_to_uid(TargetRole, pt_149, 14923, [?ERRCODE(err149_other_busy), ActId, Player#player_status.id, ?MATE_RESPONSE_YES]),
            Player;
        {false, Err} ->
            lib_server_send:send_to_uid(TargetRole, pt_149, 14923, [Err, ActId, Player#player_status.id, ?MATE_RESPONSE_YES]),
            Player;
        true ->
            #player_status{id = RoleId, scene = _SceneId, scene_pool_id = _ScenePoolId, copy_id = _CopyId, x = _X, y = _Y} = Player,
            %% 将对方拉到我的身边来
            ActLabel = act_label(ActId),
            lib_scene:change_act_label(TargetRole, ActLabel, 1),
            lib_player:setup_action_lock(TargetRole, ?ERRCODE(err149_on_game)),
%%
%%            case TargetRoleScene =:= SceneId andalso TargetRoleScenePoolId =:= ScenePoolId andalso abs(X-TargetRoleX) < 1200 andalso abs(Y-TargetRoleY) < 1200 of
%%                true  ->
%%                    lib_scene:change_act_label(TargetRole, ActLabel, 1),
%%                    lib_player:setup_action_lock(TargetRole, ?ERRCODE(err149_on_game)),
%%                    ok;
%%                false ->
%%                    lib_scene:change_player_scene(TargetRole, SceneId, ScenePoolId, CopyId, X, Y,
%%                        [{act_label, [{ActLabel, 1}]}, {action_lock, ?ERRCODE(err149_on_game)}])
%%            end,
            DailyScore = lib_daily:get_count(RoleId, ?MOD_MATE, ActId),
            mate_mgr:mate_act_response(Player#player_status.id, TargetRole, ActId, ?MATE_RESPONSE_YES,
                [{RoleId, DailyScore}|DailyScoreList]),
            lib_server_send:send_to_uid(TargetRole, pt_149, 14923, [1, ActId, Player#player_status.id, ?MATE_RESPONSE_YES]),
            Player1 = lib_scene:change_act_label(Player, ActLabel, 1),
            lib_player:setup_action_lock(Player1, ?ERRCODE(err149_on_game))
    end.

%%find_ok_position(SceneId, X, Y) ->
%%    case scene_landform:is_block_logic(SceneId, X+400, Y) of
%%        false -> {X+400, Y};
%%        true  ->
%%            case scene_landform:is_block_logic(SceneId, X-400, Y) of
%%                false -> {X-400, Y};
%%                true  ->
%%                    case scene_landform:is_block_logic(SceneId, X, Y+400) of
%%                        false -> {X, Y+400};
%%                        true  -> {X, Y-400}
%%                    end
%%            end
%%    end.
%% 取消邀请
mate_act_cancel_invite(Player) ->
    mate_mgr:mate_act_cancel_invite(Player#player_status.id).

%% 准备状态
mate_act_get_ready_state(Player, MateRole) ->
    mate_mgr:mate_act_get_ready_state(Player#player_status.id, MateRole, Player#player_status.sid).
%% 准备
mate_act_get_ready(Player, MateRole) ->
    mate_mgr:mate_act_get_ready(Player#player_status.id, MateRole).

act_label(?MATE_ACT_RHYTHM_MASTER) -> ?ACT_LABEL_MATE_RHYTHM_MASTER;
act_label(?MATE_ACT_CATCH_BALL) -> ?ACT_LABEL_MATE_CATCH_BALL.
%%---------------------------------------------------------------------
%% @doc 节奏大师 点击拍点
%%---------------------------------------------------------------------
mate_rhythm_tap(Player, TapId, MateRole) ->
    mate_mgr:rhythm_master_tap(Player#player_status.id, MateRole, TapId),
    ok.
%%---------------------------------------------------------------------
%% @doc 抛接球
%%---------------------------------------------------------------------
%% 抛接球 开球
mate_catch_ball_begin(Player, MateRole) ->
    mate_mgr:mate_catch_ball_begin(Player#player_status.id, MateRole),
    ok.
%% 抛接球 击球
mate_catch_ball_catch(Player, MateRole) ->
    mate_mgr:mate_catch_ball_catch(Player#player_status.id, MateRole),
    ok.
%%---------------------------------------------------------------------
%% @doc GM结算
%%---------------------------------------------------------------------
gm_end(RoleIdA, RoleIdB) ->
    mate_mgr:mate_act_quit(RoleIdA, RoleIdB).
gm_end(RoleId) ->
    lib_player:apply_cast(RoleId, ?APPLY_CAST_STATUS, ?MODULE, gm_stop, []).
gm_stop(Player) ->
    #player_status{mate = #role_mate{mate_list = MateList}, id = RoleId} = Player,
    [
        mate_mgr:mate_act_quit(RoleId, MateRole)
        || #mate_info{id = MateRole} <- MateList
    ],
    Player.

gm_refresh_task(Player) ->
    #player_status{mate = RoleMate} = Player,
    #role_mate{mate_list = MateList} = RoleMate,
    if
        MateList == [] ->
            {ok, Player};
        true ->
            case update_mate_daily(Player, []) of
                {ok, NewPlayer} -> {ok, NewPlayer};
                _ -> {ok, Player}
            end
    end.
%%---------------------------------------------------------------------
%% @doc 判断能否结成羁绊
%%---------------------------------------------------------------------
%% 条件：自己羁绊上限/双方好友/双方亲密度/对方在线/自己等级/对方等级/是否已经发出了请求/对方羁绊是否已达上限
check_be_mate(Player, ToRole) ->
    case ask_mate_check_self(Player, ToRole) of
        {false, Err} ->
            {false, Err};
        true ->
            case catch lib_player:apply_call(ToRole, ?APPLY_CALL_STATUS, ?MODULE, check_in_other, [Player#player_status.id]) of
                skip ->
                    {false, ?ERRCODE(err149_target_offline)};
                {res, Res} ->
                    Res;
                _Error ->
                    ?ERR("~p", [_Error]),
                    {false, 0}
            end
    end.
%% 自己进程判断条件符合
ask_mate_check_self(Player, ToRole) ->
    ask_mate_check_each(Player, ToRole, true).

%% 对方进程判断条件符合
check_in_other(Player, ToRole) ->
    Res = ask_mate_check_each(Player, ToRole, false),
    {{res, Res}, Player}.

%% 双方都判断一遍
ask_mate_check_each(Player, ToRole, IsAsker) ->
    #player_status{id = _RoleId, mate = #role_mate{mate_list = MateList}} = Player,
    [{ToRole, Rela, Intimacy}] = lib_relationship_api:get_rela_and_intimacy_online(Player#player_status.id, [ToRole]),
    IntimacyNeed = data_mate:get_mate_key(mate_intimacy),
    LvNeed = lib_module:get_open_lv(?MOD_MATE),
    MateLimit = data_mate:get_mate_key(mate_max),
    AlreadyMate = lists:keymember(ToRole, #mate_info.id, MateList),
    if
        %% 玩家等级
        IsAsker andalso Player#player_status.figure#figure.lv < LvNeed -> {false, ?ERRCODE(err149_less_lv_self)};
        Player#player_status.figure#figure.lv < LvNeed -> {false, ?ERRCODE(err149_less_lv)};
        %% 羁绊个数上限
        IsAsker andalso length(MateList) >= MateLimit -> {false, ?ERRCODE(err149_mates_enough_myself)};
        length(MateList) >= MateLimit -> {false, ?ERRCODE(err149_mates_enough)};
        %% 好友
        IsAsker andalso Rela =/= ?RELA_TYPE_FRIEND -> {false, ?ERRCODE(err149_not_friends_self)};
        Rela =/= ?RELA_TYPE_FRIEND -> {false, ?ERRCODE(err149_not_friends)};
        %% 亲密度
        not IsAsker andalso Intimacy < IntimacyNeed -> {false, ?ERRCODE(err149_less_intimacy)}; %% 亲密度只在对方判断
        AlreadyMate -> {false, ?ERRCODE(err149_already_mate)};
        true ->
            true
    end.

%%---------------------------------------------------------------------
%% @doc 交友大厅
%%---------------------------------------------------------------------
%% 发布交友信息
hall_release(Player, Msg, Type) ->
    IsSensitive = lib_word:check_text(Msg),
    MaxWidth = data_vsn:get_mate_declaration_width(),
    LengthOk = util:check_length(Msg, 0, MaxWidth),
    Cost = case Type of
               1  -> data_mate:get_mate_key(hall_release_normal_price);
               2  -> data_mate:get_mate_key(hall_release_sp_price)
           end,
    if
        IsSensitive -> {false, ?ERRCODE(err149_word_sensitive)};
        not LengthOk -> {false, ?ERRCODE(err149_msg_length)};
        true ->
            case lib_goods_api:cost_object_list_with_check(Player, Cost, mate_release, "") of
                {true, NewPlayer, _} ->
                    mate_mgr:mate_hall_release(Player#player_status.id, Msg, Type),
                    lib_chat:send_TV({all}, ?MOD_MATE, 3, [Player#player_status.figure#figure.name]),
                    {true, NewPlayer};
                {false, Err, _} ->
                    {false, Err}
            end
    end.

%%---------------------------------------------------------------------
%% @doc 拍照日常
%%---------------------------------------------------------------------
%% @doc 发出活动邀请
take_photo_invite(Player) ->
    #player_status{
        mate = #role_mate{mate_list = MateList},
        action_lock = ActionLock,
        id = RoleId
    } = Player,
    DailyCount = lib_daily:get_count(Player#player_status.id, ?MOD_MATE, ?MATE_COUNTER_INVITE),
    InviteList =
    [
        Id || #mate_info{id = Id} <- MateList, lib_player:is_online_global(Id)
    ],
    if
        DailyCount > 0 -> {false, ?ERRCODE(err149_photo_invite)};
        InviteList =:= [] -> {false, ?ERRCODE(err149_photo_offline)};
        ActionLock =/= free -> {false, ActionLock};
        true ->
            mate_mgr:take_photo_invite(RoleId, InviteList),
            true
    end.

%% @doc 回应邀请
take_photo_response(Player, FromRole, Pos, ?MATE_RESPONSE_NO) ->
    mate_mgr:take_photo_response(Player#player_status.id, FromRole, Pos, ?MATE_RESPONSE_NO),
    true;
take_photo_response(Player, FromRole, Pos, ?MATE_RESPONSE_YES) ->
    #player_status{id = RoleId, action_lock = ActionLock} = Player,
    if
        ActionLock > 0 -> {false, ActionLock};
        true ->
            mate_mgr:take_photo_response(RoleId, FromRole, Pos, ?MATE_RESPONSE_YES),
            true
    end.

%% @doc 拍照准备
take_photo_ready(Player, FromId, _, ?MATE_READY_NO) ->
    mate_mgr:take_photo_ready(Player#player_status.id, FromId, ?MATE_READY_NO),
    true;
take_photo_ready(Player, FromId, PosType, ?MATE_READY_YES) ->
    #player_status{
        id = RoleId,
        scene = Scene,
        scene_pool_id = PoolId,
        x = X,
        y = Y,
        action_lock = ActionLock
    } = Player,
    case data_mate:get_xy_by_id(PosType) of
        #base_mate_photo_pos{scene_id = SceneCfg, my_xy = {OwnerX, OwnerY}, others_xy = OthersXY} ->
            {XCfg, YCfg} =
            case PosType =:= 0 of
                true  -> {OwnerX, OwnerY};
                false ->
                    case lists:keyfind(PosType, 1, OthersXY) of
                        {PosType, {OtherX, OtherY}} -> {OtherX, OtherY};
                        _ -> {0, 0}
                    end
            end,
            IsInPos = umath:distance({X, Y}, {XCfg, YCfg}) < 200,
            if
                Scene =/= SceneCfg ->
                    {false, ?ERRCODE(err149_photo_wrong_scene)};
                not IsInPos ->
                    {false, ?ERRCODE(err149_photo_wrong_pos)};
                ActionLock > 0 ->
                    {false, ActionLock};
                RoleId =:= FromId ->
                    %% 邀请者直接去准备吧
                    mate_mgr:take_photo_ready(RoleId, FromId, ?MATE_READY_YES),
                    true;
                true ->
                    %% 其他人需要跟邀请者在同一条分线
                    case catch lib_player:apply_call(FromId, ?APPLY_CAST_STATUS, ?MODULE, check_invite_scene, [Scene, PoolId]) of
                        skip ->
                            {false, ?ERRCODE(err149_target_offline)};
                        false ->
                            {false, ?ERRCODE(err149_photo_away_invite)};
                        true ->
                            mate_mgr:take_photo_ready(RoleId, FromId, ?MATE_READY_YES),
                            true;
                        _ ->
                            {false, ?FAIL}
                    end
            end;
        _ ->
            true
    end.

%%---------------------------------------------------------------------
%% @doc 羁绊日常
%%---------------------------------------------------------------------
%% 获取任务信息
get_task_info(Player, [{false, ErrCode}]) ->
    lib_server_send:send_to_sid(Player#player_status.sid, pt_149, 14953, [ErrCode, [], []]),
    {ok, Player};
get_task_info(Player, _) ->
    #player_status{sid = Sid, mate = RoleMate} = Player,
    #role_mate{mate_daily = MateDaily} = RoleMate,
    #mate_daily{rela_info = RelaInfo, stranger_info = StrangerInfo} = MateDaily,
    MateList = [begin
                    case lib_role:get_role_figure(RoleId) of
                        #figure{} = RoleInfo ->skip;
                        _ -> RoleInfo = #figure{}
                    end,
                    OnlineFlag =
                    case ets:lookup(?ETS_ONLINE, RoleId) of
                        []  -> 0;
                        _ -> 1
                    end,
                    {RoleId, RoleInfo, TaskId, Progress, State, OnlineFlag}
                end || {RoleId, TaskId, Progress, State}<- RelaInfo],
    StrangerList = [begin
                    case lib_role:get_role_figure(RoleId) of
                        #figure{} = RoleInfo ->skip;
                        _ -> RoleInfo = #figure{}
                    end,
                    {RoleId, RoleInfo}
                end || RoleId <- StrangerInfo],
    lib_server_send:send_to_sid(Sid, pt_149, 14953, [?SUCCESS, MateList, StrangerList]),
    {ok, Player}.

%% 领取任务完成奖励
receive_task_reward(Player, [{false, ErrCode}]) ->
    lib_server_send:send_to_sid(Player#player_status.sid, pt_149, 14955, [ErrCode, 0]),
    {ok, Player};
receive_task_reward(Player, [TargetId]) ->
    #player_status{id = RoleId, sid = Sid, mate = RoleMate} = Player,
    #role_mate{mate_daily = MateDaily} = RoleMate,
    #mate_daily{rela_info = RelaInfo} = MateDaily,
    Result =
    case lists:keyfind(TargetId, 1, RelaInfo) of
        {TargetId, _TaskId, _Progress, ?REWARD_STATE_0} ->
            {false, ?ERRCODE(err149_task_not_final)};
        {TargetId, _TaskId, _Progress, ?REWARD_STATE_2} ->
            {false, ?ERRCODE(err149_reward_alreday_receive)};
        {TargetId, TaskId, Progress, _State} ->
            case data_mate:get_daily_task(TaskId) of
                #base_mate_daily_task{reward = Reward} ->
                    NRelaInfo = lists:keystore(TargetId, 1, RelaInfo, {TargetId, TaskId, Progress, 2}),
                    SQL = io_lib:format(?SQL_MATE_UPDATE_TASK_ONE, [Progress, ?REWARD_STATE_2, RoleId, TargetId]),
                    db:execute(SQL),
                    NMateDaily = MateDaily#mate_daily{rela_info = NRelaInfo},
                    Player1 = Player#player_status{mate = RoleMate#role_mate{mate_daily = NMateDaily}},
                    NewPlayer = lib_goods_api:send_reward(Player1, Reward, mate_daily_task),
                    {ok, NewPlayer};
                _ ->
                    {false, ?ERRCODE(missing_config)}
            end;
        _ ->
            {false, ?ERRCODE(fail)}
    end,
    case Result of
        {false, Err} ->
            lib_server_send:send_to_sid(Player#player_status.sid, pt_149, 14955, [Err, TargetId]),
            {ok, Player};
        {ok, NewPlayer2} ->
            lib_server_send:send_to_sid(Sid, pt_149, 14955, [?SUCCESS, TargetId]),
            {ok, NewPlayer2}
    end.

refresh_mate_daily(Player, Args) ->
    #player_status{mate = RoleMate} = Player,
    #role_mate{mate_list = MateList, mate_daily = MateDaily} = RoleMate,
    if
        MateList == [] ->
            {false, ?ERRCODE(err149_not_mate)};
        true ->
            #mate_daily{time = Time} = MateDaily,
            % 当天0点时间戳
            ZeroTime = utime:unixdate(),
            if
                Time > ZeroTime -> {ok, Player};
                true -> update_mate_daily(Player, Args)
            end
    end.


%% @doc 刷新羁绊日常信息
update_mate_daily(Player, Args) ->
    #player_status{id = RoleId, mate = #role_mate{mate_list = MateList}} = Player,
    % 羁绊列表
    MateIdList = [Id || #mate_info{id = Id} <- MateList],
    MateLength = length(MateIdList),
    Num = ?MATE_OBJECT_NUMBER + ?MATE_STRANGER_NUMBER,
    NowTime = utime:unixtime(),
    DiffTime = NowTime - 24 * 60 * 60,
    % 好友列表
    AllFriList = get_friend_list(RoleId),
    %% 优先在线玩家，然后留存玩家
    F = fun(Id, {Ids1,Ids2}) ->
        case lists:member(Id, MateIdList) of
            true -> {Ids1, Ids2};
            false ->
                case ets:lookup(?ETS_ONLINE, Id) of
                    []  ->
                        case ets:lookup(?ETS_ROLE_LOGOUT_TIME, Id) of
                            [{Id, LogoutTime}] when LogoutTime >= DiffTime ->
                                {Ids1, [Id | Ids2]};
                            _ -> {Ids1, Ids2}
                        end;
                    _ ->
                        {[Id | Ids1], Ids2}
                end
        end
        end,
    {OnlineList, OffList} = lists:foldl(F, {[], []}, AllFriList),
    FriList =  ulists:list_shuffle(OnlineList) ++ ulists:list_shuffle(OffList),
    FriLength = length(FriList),
    if
        MateLength + FriLength >= Num ->
            SubLen = Num - MateLength,
            SubList = lists:sublist(FriList, SubLen),
            update_mate_daily_info(Player, MateIdList ++ SubList, Args);
        true ->
            put(temp_mate_daily_data, MateIdList ++ FriList),
            put(mate_daily_args, Args),
            mod_common_rank:send_top_30_combat_rank(self()),
            skip
    end.

update_mate_daily_info(Player, IdList, Args) ->
    #player_status{id = RoleId, mate = RoleMate, role_coupon = RoleCoupon} = Player,
    #role_mate{mate_list = MateList} = RoleMate,
    #role_rela_coupon{send_list = SendList} = RoleCoupon,

    {SubList, RemainList} = ulists:sublist(IdList, ?MATE_OBJECT_NUMBER),
    FriList = get_friend_list(RoleId),
    TaskList = rand_task(SubList, MateList, FriList, SendList),
    Predicate = fun({_, null, _, _}) ->
              false;
           (_)->
              true
        end,
    Result = lists:all(Predicate, TaskList),
    if
        Result ->
            NowTime = utime:unixtime(),
            %% 数据库操作
            db:execute(?SQL_MATE_DELETE_DAILY_TASK, [RoleId]),
            db_replace_into_mate_daily(RoleId, TaskList),
            db:execute(?SQL_MATE_UPDATE_DAILY_INFO, [RoleId, NowTime, util:term_to_bitstring(RemainList)]),

            MateDaily = #mate_daily{time = NowTime, rela_info = TaskList, stranger_info = RemainList},
            NewRoleMate = RoleMate#role_mate{mate_daily = MateDaily},
            NewPlayer = Player#player_status{mate = NewRoleMate},
            case Args of
                {Mod, Fun, FunArgs} ->
                    {ok, NewPlayer2} = Mod:Fun(NewPlayer, FunArgs),
                    {ok, NewPlayer2};
                _ ->
                    {ok, NewPlayer}
            end;
        true ->
            case Args of
                {Mod, Fun, _FunArgs} ->
                    Mod:Fun(Player, [{false, ?ERRCODE(err400_not_config)}]),
                    {ok, Player};
                _ ->
                    {ok, Player}
            end
    end.

get_friend_list(RoleId) ->
    RoleList = lib_relationship:get_rela_list(RoleId, ?RELA_LIST_TYPE_FRIEND),
    F = fun(RelaInfo, List) ->
        OppRoleId = element(1, RelaInfo),
        case lib_relationship:get_ets_rela(RoleId, OppRoleId) of
            #ets_role_rela{rela = 1} -> [OppRoleId|List];
            _ -> List
        end
        end,
    FriList = lists:foldl(F, [], RoleList),
    FriList.

%% 给羁绊对象随机任务
rand_task(SubList, MateList, FriList, SendList) ->
    {MateTask, FriTask, StrTask} = get_object_task(),
    F = fun(RoleId, List) ->
        case lists:keyfind(RoleId, #mate_info.id, MateList) of
            false ->
                case lists:member(RoleId, FriList) of
                    true ->
                        TaskId = urand:list_rand(FriTask),
                        %% 刷出任务时判断是否已完成赠送票券和羁绊点的任务
                        if
                            TaskId == ?TASK_TYPE_5 ->
                                IsHas = lists:keymember({RoleId, 1}, 1, SendList),
                                ?IF(IsHas == true,[{RoleId, TaskId, 1, 1}|List],[{RoleId, TaskId, 0, 0}|List]);
                            true ->
                                [{RoleId, TaskId, 0, 0}|List]
                        end;
                    _ ->
                        TaskId = urand:list_rand(StrTask),
                        [{RoleId, TaskId, 0, 0}|List]
                end;
            _ ->
                TaskId = urand:list_rand(MateTask),
                if
                    TaskId == ?TASK_TYPE_5 ->
                        IsHas = lists:keymember({RoleId, 1}, 1, SendList),
                        ?IF(IsHas == true,[{RoleId, TaskId, 1, 1}|List],[{RoleId, TaskId, 0, 0}|List]);
                    TaskId == ?TASK_TYPE_3 ->
                        IsHas = lists:keymember({RoleId, 2}, 1, SendList),
                        ?IF(IsHas == true,[{RoleId, TaskId, 1, 1}|List],[{RoleId, TaskId, 0, 0}|List]);
                    true ->
                        [{RoleId, TaskId, 0, 0}|List]
                end
        end
        end,
    TaskList = lists:foldl(F, [], SubList),
    TaskList.

%% 获取分好类的任务
get_object_task() ->
    TaskList = data_mate:get_all_task_id(),
    F = fun(TaskId, {List1, List2, List3}) ->
        #base_mate_daily_task{object = ObjectList} = data_mate:get_daily_task(TaskId),
        NewList1 = ?IF(lists:member(?MATE_OBJECT, ObjectList), [TaskId|List1], List1),
        NewList2 = ?IF(lists:member(?FRIEND_OBJECT, ObjectList), [TaskId|List2], List2),
        NewList3 = ?IF(lists:member(?STRANGER_OBJECT, ObjectList), [TaskId|List3], List3),
        {NewList1, NewList2, NewList3}
        end,
    {MateTask, FriTask, StrTask} = lists:foldl(F, {[],[],[]}, TaskList),
    {MateTask, FriTask, StrTask}.


%% 1:从战力榜取玩家补足羁绊和路人
%% 2:前1日留存全服玩家补足羁绊
%% 3:任意玩家补足羁绊
supply_mate_list(Player, RankList) ->
    #player_status{id = RoleId} = Player,
    NowTime = utime:unixtime(),
    DiffTime = NowTime - 24 * 60 * 60,
    IdList =
        case erlang:erase(temp_mate_daily_data) of
            undefined -> [];
            List -> List
        end,
    Args =
        case erlang:erase(mate_daily_args) of
            undefined -> [];
            ReqArgs -> ReqArgs
        end,
    F = fun(Id, {HeadList, TailList}) when Id =/= RoleId ->
        case  lists:member(Id, IdList) of
            true ->
                {HeadList, TailList};
            false ->
                case ets:lookup(?ETS_ROLE_LOGOUT_TIME, Id) of
                    [{Id, LogoutTime}] when LogoutTime >= DiffTime ->
                        {[Id | HeadList], TailList};
                    _ ->
                        {HeadList, [Id|TailList]}
                end
        end;
        (_, {HeadList, TailList}) ->
            {HeadList, TailList}
        end,
    {SubList, RemainList} = ulists:sublist(RankList, 20),
    {StayList1, LeaveList1} = lists:foldl(F, {[], []}, SubList),
    {StayList2, _LeaveList2} = lists:foldl(F, {[], []}, RemainList),
    %% 从战力前30全服前1日留存玩家中随机出玩家id,不够继续从非留存玩家中随机取
    Ids = get_ids_from_rank(StayList1, LeaveList1, StayList2, IdList),
    NewIdList = IdList ++ Ids,
    %% 人数不够从前1日留存全服玩家或全服玩家中随机
    HasList = get_id_from_all_server(RoleId, NewIdList, DiffTime),
    update_mate_daily_info(Player, HasList, Args).

%% 羁绊对象从战力前30全服前1日留存玩家中随机出玩家id；
%% 路人从战力前20全服前1日留存玩家中随机出玩家id，不够继续从非留存玩家中随机取
get_ids_from_rank(StayList1, LeaveList1, StayList2, IdList) ->
    Length = length(IdList),
    TotleNum = ?MATE_OBJECT_NUMBER + ?MATE_STRANGER_NUMBER,
    NeedNum = TotleNum - Length,
    StayList = StayList2 ++ StayList1,
    StayLen = length(StayList),
    StayLen1 = length(StayList1),
    if
        Length >= ?MATE_OBJECT_NUMBER andalso StayLen1 >= NeedNum ->
            % 羁绊对象够了，从战力前20全服前1日留存玩家中随机刷出路人
            lists:sublist(ulists:list_shuffle(StayList1), NeedNum);
        Length >= ?MATE_OBJECT_NUMBER ->
            % 羁绊对象够了，留存人数不足，继续从战力前20非留存玩家中随机刷出路人
            LeaveNum = NeedNum - StayLen1,
            List = lists:sublist(ulists:list_shuffle(LeaveList1), LeaveNum),
            StayList1 ++ List;
        true ->
            % 先从从战力前30留存玩家中补足羁绊对象，再补足路人
            NeedMateL = ?MATE_OBJECT_NUMBER - Length,
            if
                StayLen >= NeedMateL ->
                    MateList = lists:sublist(ulists:list_shuffle(StayList), NeedMateL),
                    % 羁绊对象够了，从战力前20取留存玩家中随机刷出路人
                    NStayList1 = StayList1 -- MateList,
                    Len = length(NStayList1),
                    if
                        Len >= ?MATE_STRANGER_NUMBER ->
                            % 留存人数足够，从留存玩家中取
                            StrList = lists:sublist(ulists:list_shuffle(NStayList1), ?MATE_STRANGER_NUMBER),
                            MateList ++ StrList;
                        true ->
                            % 留存人数不足，从非留存玩家中取
                            Need = ?MATE_STRANGER_NUMBER - Len,
                            StrList = lists:sublist(ulists:list_shuffle(LeaveList1), Need),
                            MateList ++ NStayList1 ++ StrList
                    end;
                true ->
                    % 羁绊对象不够，先补足路人
                    List = lists:sublist(ulists:list_shuffle(LeaveList1), ?MATE_STRANGER_NUMBER),
                    StayList ++ List
            end
    end.

%% 羁绊对象从前1日留存全服玩家或全服玩家中随机
get_id_from_all_server(RoleId, HasList, DiffTime) ->
    Num = ?MATE_OBJECT_NUMBER + ?MATE_STRANGER_NUMBER,
    HasLen = length(HasList),
    if
        HasLen >= Num ->
            %% 人数够了
            HasList;
        true ->
            %% 还缺人，先从前1日留存全服玩家取
            FilterList = filter_ids(DiffTime, [RoleId|HasList]),
            FilterLength = length(FilterList),
            LeftNum = Num - HasLen,
            if
                FilterLength >= LeftNum ->
                    RandList = urand:get_rand_list(LeftNum, FilterList),
                    RandList ++ HasList;
                true ->
                    %% 人数不够从全服玩家中取
                    First = ets:first(?ETS_ROLE_LOGOUT_TIME),
                    SplicingList = FilterList ++ HasList,
                    get_all_ids(First, LeftNum - FilterLength, SplicingList)
            end
    end.


%% 过滤得到前1日留存全服玩家id
filter_ids(DiffTime, HasList) ->
    Mspec = [{{'$1','$2'},[{'>=','$2',{const,DiffTime}}],['$1']}],
    Ids = ets:select(?ETS_ROLE_LOGOUT_TIME, Mspec),
    Ids -- HasList.

get_all_ids(_, 0, HasList) ->
    HasList;
get_all_ids(LastKey, Num, HasList) ->
    case lists:member(LastKey, HasList) of
        true when is_integer(LastKey) ->
            NextKey = ets:next(?ETS_ROLE_LOGOUT_TIME, LastKey),
            get_all_ids(NextKey, Num, HasList);
        false when is_integer(LastKey) ->
            NextKey = ets:next(?ETS_ROLE_LOGOUT_TIME, LastKey),
            get_all_ids(NextKey, Num - 1, [LastKey|HasList]);
        _ ->
            HasList
    end.

%% 更新羁绊任务进度
update_take_progress(Player, [Id, Type]) ->
    #player_status{id = RoleId, sid = Sid, mate = RoleMate} = Player,
    #role_mate{mate_list = MateList} = RoleMate,
    if
        MateList == []  ->
            %% 没有羁绊忽略
            {ok, Player};
        true ->
            case mate_logic:refresh_mate_daily(Player, {mate_logic, update_take_progress, [Id, Type]}) of
                {ok, NewPlayer} ->
                    #player_status{mate = RoleMate2} = NewPlayer,
                    #role_mate{mate_daily = MateDaily2} = RoleMate2,
                    #mate_daily{rela_info = RelaInfo} = MateDaily2,
                    F = fun({OtherId, TaskId, Progress, State}, {Info, UpdateList}) when Id == OtherId orelse Id == 0 ->
                        if
                            State == 0 ->
                                case data_mate:get_daily_task(TaskId) of
                                    #base_mate_daily_task{type = Type, count = Count} ->
                                        NProgress = Progress + 1,
                                        NState = ?IF(NProgress >= Count, 1, 0),
                                        NewInfo = lists:keystore(OtherId, 1, Info, {OtherId, TaskId, NProgress, NState}),
                                        %% 写库
                                        SQL = io_lib:format(?SQL_MATE_UPDATE_TASK_ONE, [NProgress, NState, RoleId, OtherId]),
                                        db:execute(SQL),
                                        {NewInfo, [{OtherId, TaskId, NProgress, NState}|UpdateList]};
                                    _ ->
                                        {Info, UpdateList}
                                end;
                            true ->
                                {Info, UpdateList}
                        end;
                        (_, {Info, UpdateList}) ->
                            {Info, UpdateList}
                        end,
                    {NRelaInfo, UpdateList} = lists:foldl(F, {RelaInfo, []}, RelaInfo),
                    if
                        UpdateList == [] ->
                            {ok, Player};
                        true ->
                            NMateDaily = MateDaily2#mate_daily{rela_info = NRelaInfo},
                            NRoleMate = RoleMate2#role_mate{mate_daily = NMateDaily},
                            NewPlayer2 = NewPlayer#player_status{mate = NRoleMate},
                            lib_server_send:send_to_sid(Sid, pt_149, 14954, [UpdateList]),
                            {ok, NewPlayer2}
                    end;
                _ ->
                    {ok, Player}
            end
    end.


%% 玩家是否在目标场景
check_invite_scene(Player, OtherScene, OtherPoolId) ->
    {
        {Player#player_status.scene =:= OtherScene andalso Player#player_status.scene_pool_id =:= OtherPoolId},
        Player
    }.

%% 领取新手奖励
get_newbee_reward(Player) ->
    #player_status{mate = #role_mate{newbee = Newbee} = RoleMate, id = RoleId} = Player,
    case Newbee == 0 of
        true  ->
            NewRoleMate = RoleMate#role_mate{newbee = 1},
            db_save_role_mate(RoleId, NewRoleMate),
            Player1 =
            case data_mate:get_mate_key(advance_rewards) of
                [_|_] = Award ->
                    lib_goods_api:send_reward(Player, Award, mate_advance_rewards);
                _ ->
                    Player
            end,
            NewPlayer = Player1#player_status{mate = NewRoleMate},
            {true, NewPlayer};
        false ->
            ok
    end.
%%---------------------------------------------------------------------
%% @doc 数据库操作
%%---------------------------------------------------------------------
%% 保存羁绊系统信息
db_save_role_mate(RoleId, RoleMate) ->
    #role_mate{
        boat_stage = BoatLv
        , boat_star = BoatStar
        , boat_exp = BoatExp
        , wear_mate = WearMate
        , newbee = Newbee
    } = RoleMate,
    db:execute(?SQL_MATE_SAVE_MATE_ROLE_INFO, [RoleId, BoatLv, BoatStar, BoatExp, WearMate, Newbee]).
%% 保存单个羁绊伙伴
db_save_one_mate(RoleId, OneMate) ->
    #mate_info{
        id = Id,
        remarks = Remarks,
        title = Title,
        time = Time,
        anniversary = Anniversary
    } = OneMate,
    db:execute(?SQL_MATE_SAVE_ONE_MATE, [RoleId, Id, Remarks, Title, Time, util:term_to_bitstring(Anniversary)]).
db_del_mate(RoleId, TargetRole) ->
    db:execute(?SQL_MATE_DELETE_ONE_MATE, [RoleId, TargetRole]).

%% 批量回写
db_replace_into_mate_daily(RoleId, DataList) ->
    DataString = util:join_bitstring([fmt_data_string(RoleId, Data) || Data <- DataList], <<",">>),
    case DataString =:= <<>> of
        true -> ok;
        false ->
            Sql = util:fmt_bin(?SQL_MATE_UPDATE_DAILY_TASK, [DataString]),
            db:execute(Sql)
    end.

fmt_data_string(RoleId, {OtherId, TaskId, Progress, State}) ->
    util:fmt_bin("(~w, ~w, ~w, ~w, ~w)", [RoleId, OtherId, TaskId, Progress, State]).
