%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%     装备处理逻辑
%%% @end
%%%-------------------------------------------------------------------
-module(equipment_logic).
-author("liushl").

-include("server.hrl").
-include("def_goods.hrl").
-include("goods.hrl").
-include("equipment.hrl").
-include("errcode.hrl").
-include("figure.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").
-include("common.hrl").
-include("market.hrl").
-include("def_module.hrl").
-include("language.hrl").
-include("scene.hrl").
-include("local_boss.hrl").
%% API
-export([
    equipment_basic_info/1
    , equipment_wear/4               %% 装备穿戴
    , equipment_off/2                %% 装备卸
    , equipment_strength/4           %% 强化装备
    , equipment_rise_star/6          %% 装备升星
    , equipment_active_star/2        %% 激活星级套装
    , equipment_unlock_series/1      %% 解锁装备代数
    , equipment_empower/3            %% 器灵升级
    , rise_star_preview/4            %% 进化预览
    , equipment_gems_inlay/4         %% 宝石镶嵌
    , equipment_gems_unload/4        %% 宝石卸下
    , equipment_gems_upgrade/4       %% 宝石升级-对于已镶嵌的
    , equipment_gems_active_suit/2   %% 激活宝石套装
    , auto_set_gems/2
    , equipment_follow_guard/3       %% 守护跟随状态
    , check_follow_guard/2           %% 判断守护跟随
    , save_equipment_series_pos_info/2
    , save_equipment_info/2

    , kill_personal_boss/1          %% 可成长属性击杀BOSS成长
    , kill_personal_boss/2
    , kill_reward_boss/1
    , kill_deserted_boss/1
    , kill_home_boss/1
    , help_kill_reward_boss/1
    , help_kill_deserted_boss/1
    , calc_sum_lv/1
]).

%%---------------------------------------------------------------------
%% @doc 基本信息
%%---------------------------------------------------------------------
equipment_basic_info(Player) ->
    #player_status{equipment = Equipment} = Player,
    #equipment{stars = Stars, stars_info = StarsInfo,unlock_series = UnlockSeries} = Equipment,
    [Stars, UnlockSeries, StarsInfo].
%%---------------------------------------------------------------------
%% @doc 装备穿戴 是否存在/套数/位置类型/等级/职业限制/属性替换
%%---------------------------------------------------------------------
equipment_wear_check(Player, Series, Pos, GoodsId) ->
    case common_check(GoodsId, ?GOODS_LOC_BAG) of
        {false, Err} -> {false, Err};
        {ok, FromGoods, EquipConfig} ->
            NeedCareer = EquipConfig#base_equipment.career,
            CareerLimit = lists:member(Player#player_status.figure#figure.career, NeedCareer),
            Cell = ?EQUIPMENT_CELL(Series, Pos),
            OldExistGoods = lib_goods_util:get_goods_by_cell(?GOODS_LOC_EQUIP, Cell),
            EmpowerLv = equipment_api:get_equipment_empower(Player, Series, Pos),
            UnlockSeries = transform:get_unlock_equipment_series(Player),
            if
                Pos =/= EquipConfig#base_equipment.equip_type -> {false, ?ERRCODE(err152_equip_pos_err)};
                Player#player_status.figure#figure.lv < EquipConfig#base_equipment.lv -> {false, ?ERRCODE(err152_lv_err)};
                Series =/= EquipConfig#base_equipment.series -> {false, ?ERRCODE(err152_series_err)};
                NeedCareer =/= [] andalso not CareerLimit -> {false, ?ERRCODE(err152_career_error)};
                Series > UnlockSeries -> {false, ?ERRCODE(err152_series_lock)};
                %% 器灵升级后不可更换低品质装备
                is_record(OldExistGoods, goods) andalso EmpowerLv > 0
                    andalso FromGoods#goods.color < OldExistGoods#goods.color -> {false, ?ERRCODE(err152_cannot_equip_lower_color)};
                true ->
                    {ok, FromGoods}
            end
    end.

%% 装备穿戴
equipment_wear(Player, Series, Pos, GoodsId) ->
    case equipment_wear_check(Player, Series, Pos, GoodsId) of
        {false, Err} -> {false, Err};
        {ok, FromGoods} ->
            equipment_wear_do(Player, Series, Pos, FromGoods)
    end.
equipment_wear_do(Player, Series, Pos, FromGoods) ->
    #player_status{equipment = #equipment{equipment_series = SeriesInfo} = Equipment, id = RoleId} = Player,
    Cell = ?EQUIPMENT_CELL(Series, Pos),
    OldExistGoods = lib_goods_util:get_goods_by_cell(?GOODS_LOC_EQUIP, Cell),
    {NewGoodsInfo, OldGoodsInfo} = check_auto_inherit(Player, FromGoods, OldExistGoods),
    %% 穿戴强制绑定
    goods_logic:put_goods_on(NewGoodsInfo, ?GOODS_LOC_BAG, ?GOODS_LOC_EQUIP, Cell, OldGoodsInfo),
    %% 自动继承的话做日志
    case OldGoodsInfo of
        #goods{num = 0, goods_id = CostGoods} ->
            log_equipment_inherit(Player, 2, FromGoods, NewGoodsInfo, CostGoods),
            lib_log_api:log_throw(equipment_auto_inherit, RoleId, OldGoodsInfo#goods.id, OldGoodsInfo#goods.goods_id, 1, 0, 0);
        _ ->
            ok
    end,
    {ok, Player2} = lib_player_event:dispatch(Player, ?EVENT_EQUIP, equipment_api:get_all_equipment()),
    lib_achievement_api:own_equip_event(Player, equipment_api:get_equipment_wear_info()),
    lib_seven_days_goal:finish_task(RoleId, ?MOD_EQUIP, 8, 1, true),
    lib_achievement_api:equip_wear_event(Player, equipment_api:get_equipment_wear_info1()),
    StrenList = equipment_api:get_equipment_strength_info(),
    lib_task_api:stren_equip(Player,StrenList),

    StarNum = equipment_api:include_equipment_stars(),
    lib_task_api:rise_equip(Player,StarNum),
    lib_seven_days_goal:finish_task(RoleId, ?MOD_EQUIP, 0, StarNum, true),
    lib_seven_days_goal:finish_task(RoleId, ?MOD_EQUIP, 9, 1, true),

    lib_task_api:suit_equip(Player),
    ta_agent_fire:equip_wear(Player, Series, NewGoodsInfo, OldGoodsInfo),
    %% 新的橙装数量:穿戴一件橙色普通装备，旧的不是橙装才需要判断
    IsNormalEquipment = lists:member(NewGoodsInfo#goods.subtype, ?EQUIPMENT_NORMAL),
    NeedCheckTv =
    if
        not IsNormalEquipment ->
            false;
        NewGoodsInfo#goods.color < ?ORANGE ->
            false;
        not is_record(OldExistGoods, goods) ->
            true;
        OldExistGoods#goods.color >= ?ORANGE ->
            false;
        true ->
            true
    end,
    case NeedCheckTv of
        true ->
            NewNum = equipment_api:get_series_equipment_num(Series, ?ORANGE),
            case NewNum =:= 7 of
                true  ->
                    Name = Player#player_status.figure#figure.name,
                    lib_chat:send_TV({all}, ?MOD_EQUIP, ?LAN_TV_EQUIPMENT_SUIT, [Name, Series]);
                false ->
                    ok
            end;
        false ->
            ok
    end,
    Player3 = equipment_collect:check_collect(Player2, NewGoodsInfo),
    Player4 = check_follow_guard(Player3, NewGoodsInfo),
    %% 第一次穿戴特殊装备，需要写入sp_attr
    Player5 =
    case lists:member(Pos, ?EQUIPMENT_SPECIAL) andalso (not is_record(OldExistGoods, goods)) of
        true  ->
            SeriesPos = equipment_api:get_equipment_series_pos_info(SeriesInfo, Series, Pos),
            NewSeriesInfo = equipment_api:set_equipment_series_pos_info(SeriesInfo, SeriesPos),
            save_equipment_series_pos_info(RoleId, SeriesPos),
            Player4#player_status{equipment = Equipment#equipment{equipment_series = NewSeriesInfo}};
        false ->
            Player4
    end,
    %% 新手定制装备，穿戴的时候如没有升品，则升品+1
    Player6 =
        case FromGoods#goods.goods_id =:= 24191015 orelse FromGoods#goods.goods_id =:= 24291015 of
            true  ->
                case equipment_api:get_equipment_empower(Player, Series, Pos) of
                    0 ->
                        PlayerEmpower = empower_data_save(Player5, Series, Pos, 1),
                        pp_equipment:handle(15208, PlayerEmpower, []),
                        PlayerEmpower;
                    _ ->
                        Player5
                end;
            false ->
                Player5
        end,
    PlayerLast = equipment_api:update_equipment_power(Player6),
    {ok, PlayerLast}.

%% 判断守护跟随
check_follow_guard(Player, #goods{subtype = ?EQUIPMENT_GUARD, goods_id = GoodsId, id = Id}) ->
    #player_status{
        equipment = RoleEquipment
    } = Player,
    case RoleEquipment#equipment.follow_guard =/= GoodsId of
        true  ->
            update_follow_guard(Player, Id, GoodsId, 1);
        false ->
            Player
    end;
check_follow_guard(Player, _GoodsInfo) ->
    Player.
%%---------------------------------------------------------------------
%% @doc 装备卸 是否存在/背包大小
%%---------------------------------------------------------------------
equipment_off_check(_Player, GoodsId) ->
    case common_check(GoodsId, ?GOODS_LOC_EQUIP) of
        {false, Err} -> {false, Err};
        {ok, FromGoods, _EquipConfig} ->
            CellNum = lib_goods_util:get_null_cell_num(?GOODS_LOC_BAG),
            if
                CellNum =< 0 -> {false, ?ERRCODE(err150_no_cell)};
                true ->
                    {ok, FromGoods}
            end
    end.
equipment_off(Player, GoodsId) ->
    case equipment_off_check(Player, GoodsId) of
        {false, Err} -> {false, Err};
        {ok, FromGoods} ->
            equipment_off_do(Player, FromGoods)
    end.
equipment_off_do(Player, FromGoods) ->
    goods_logic:take_goods_off(FromGoods, ?GOODS_LOC_BAG),
    Player1 = equipment_api:update_equipment_power(Player),
    lib_achievement_api:equip_wear_event(Player1, equipment_api:get_equipment_wear_info1()),
    {ok, Player1}.
%%---------------------------------------------------------------------
%% @doc 强化装备 是否存在/穿着才行/守护不能强化/是否满级
%%---------------------------------------------------------------------
equipment_strength_check(_Player, GoodsId, Location, _AutoBuy) ->
    case common_check(GoodsId, Location) of
        {false, Err} -> {false, Err};
        {ok, GoodsInfo, _EquipConfig} ->
            Lv = equipment_api:get_equipment_strength_lv(GoodsInfo),
            Series = equipment_api:get_equipment_series(GoodsInfo),
            StrengthConfig = data_equipment:get_equipment_strength(Series, GoodsInfo#goods.subtype, Lv+1),
            if
                Location =/= ?GOODS_LOC_EQUIP -> {false, ?ERRCODE(err152_must_wear)};
                GoodsInfo#goods.subtype =:= ?EQUIPMENT_GUARD -> {false, ?ERRCODE(err152_cannot_be_guard)};
                not is_record(StrengthConfig, base_equipment_strength) -> {false, ?ERRCODE(err152_stren_limit)};
                true ->
                    {ok, GoodsInfo, StrengthConfig, Lv+1}
            end
    end.

%% 强化装备
equipment_strength(Player, GoodsId, Location, AutoBuy) ->
    case equipment_strength_check(Player, GoodsId, Location, AutoBuy) of
        {false, Err} -> {false, Err};
        {ok, GoodsInfo, StrengthConfig, Lv} ->
            Res =
            case AutoBuy of
                1 ->
                    lib_goods_api:cost_objects_with_auto_buy(Player, StrengthConfig#base_equipment_strength.cost, equipment_strength, "");
                _ ->
                    lib_goods_api:cost_object_list_with_check(Player, StrengthConfig#base_equipment_strength.cost, equipment_strength, "")
            end,
            case Res of
                {false, Err, _} -> {false, Err};
                {true, Player1, _} ->
                    Ratio = equipment_api:get_equipment_strength_success_ratio(GoodsInfo, StrengthConfig#base_equipment_strength.ratio),
                    AddRatio = lib_pet_api:pet_life_skill_about_equip_strength_add(Player),
                    AddRatio2 = lib_recharge_welfare:equip_strength_add(Player),
                    FinalRatio = Ratio + AddRatio * 100 + AddRatio2 * 100,
                    Success = FinalRatio >= urand:rand(1,10000),
                    case Success of
                        true  ->
                            ta_agent_fire:equip_strengthen(Player, GoodsInfo, FinalRatio, true, Lv),
                            log_equipment_strength(Player1, StrengthConfig, GoodsInfo, 1, Lv-1, Lv, Ratio);
                        false ->
                            ta_agent_fire:equip_strengthen(Player, GoodsInfo, FinalRatio, false, Lv - 1),
                            log_equipment_strength(Player1, StrengthConfig, GoodsInfo, 0, Lv-1, Lv-1, Ratio)
                    end,
                    equipment_strength_do(Success, Player1, GoodsInfo, StrengthConfig, Lv)
            end
    end.
equipment_strength_do(false, Player, GoodsInfo, StrengthConfig, _NextLv) ->
    Ratio = equipment_api:get_equipment_strength_success_ratio(GoodsInfo, StrengthConfig#base_equipment_strength.ratio),
    NewGoodsInfo = goods_extra_data:extra_data_update(GoodsInfo,
        [{?GOODS_INFO_EQUIPMENT_SUCCESS_RATIO, min(10000, Ratio+StrengthConfig#base_equipment_strength.fail_add_ratio)}]),
    goods_logic:update_goods_info_extra([NewGoodsInfo]),
    {fail, Player};
equipment_strength_do(true, Player, GoodsInfo, _StrengthConfig, NextLv) ->
    ExtraData1 = lists:keydelete(?GOODS_INFO_EQUIPMENT_SUCCESS_RATIO, 1, GoodsInfo#goods.extra_data),
    NewGoodsInfo = goods_extra_data:extra_data_update(GoodsInfo#goods{extra_data = ExtraData1}, [{?GOODS_INFO_EQUIPMENT_LV, NextLv}]),
    goods_logic:update_goods_info_extra([NewGoodsInfo]),
    {ok, Player1} = lib_player_event:dispatch(Player, ?EVENT_EQUIP, equipment_api:get_all_equipment()),
    Player2 = equipment_api:update_equipment_power(Player1),
    %% 成就
    StrenList = equipment_api:get_equipment_strength_info(),
    lib_achievement_api:equip_stren_up_event(Player, StrenList),
    lib_task_api:stren_equip(Player,StrenList),
    lib_custom_act_slices:task_event(Player2,?MOD_EQUIP,1),
    custom_act_mobilize:update_mobilize_task(Player, strength, 1),
    % {ok, Player3} = lib_seven_days_goal:do_finish_task(Player2, ?MOD_EQUIP, 1),
    % {ok, Player4} = lib_seven_days_goal:special_task_handle(Player3, equipment_strength, 1),
    %% 传闻
    case NextLv >= 7 of
        true  ->
            #player_status{id = RoleId, figure = #figure{name = Name}} = Player,
            #goods{goods_id = GoodsId, id = Id} = NewGoodsInfo,
            lib_chat:send_TV({all}, ?MOD_EQUIP, ?LAN_TV_EQUIPMENT_STRENGTH, [Name, RoleId, Id, GoodsId, NextLv]);
        false ->
            ok
    end,
    {ok, Player2, NextLv}.

%%---------------------------------------------------------------------
%% @doc 装备升星 穿着才行/首饰和守护不能升星/是否满星/品质限制
%%---------------------------------------------------------------------
make_goods_all_good(GoodsList, Series, NextLv, Career) ->
    make_goods_all_good(GoodsList, Series, NextLv, Career, [], 0).
make_goods_all_good([], _Series, _NextLv, _Career, List, Ratio) -> {List, Ratio};
make_goods_all_good([GoodsId|T], Series, NextLv, Career, List, Ratio) ->
    case lib_goods_api:get_goods_info(GoodsId, ?GOODS_LOC_BAG) of
        #goods{goods_id = GoodsTypeId, color = Color, type = ?GOODS_TYPE_EQUIP} = GoodsInfo ->
            GoodsSeries = equipment_api:get_equipment_series(GoodsTypeId),
            CareerOk = equipment_api:check_equipment_career(GoodsTypeId, Career),
            case data_equipment:get_great_add_ratio(GoodsSeries, Color, Series, NextLv) of
                [AddRatio] when CareerOk ->
                    make_goods_all_good(T, Series, NextLv, Career, [{GoodsInfo, 1}|List], Ratio + AddRatio);
                _ ->
                    false
            end;
        _ ->
            false
    end.

equipment_rise_star_check(Player, GoodsId, Location, CostGoodsList, MarketBuy, _AutoBuy) ->
    case common_check(GoodsId, Location) of
        {false, Err} -> {false, Err};
        {ok, GoodsInfo, EquipmentConfig} ->
            #player_status{id = RoleId, figure = #figure{career = Career}} = Player,
            #base_equipment{
                series = Series,
                equip_type = EquipType
            } = EquipmentConfig,
            IsCanGreat = equipment_api:is_can_great_equip(GoodsInfo),
            %% 当前进化等级
            GreatLv = equipment_api:get_equipment_great_lv(GoodsInfo),
            NextLv = GreatLv + 1,
            %% 下一级配置
            EquipmentGreatConfig = data_equipment:get_equipment_great(Series, EquipType, NextLv),
            CheckCostGoods = make_goods_all_good(CostGoodsList, Series, GreatLv+1, Career),
            if
                not IsCanGreat ->
                    %% 普通装备才能进化
                    {false, ?ERRCODE(err152_cannot_rise_star)};
                not is_record(EquipmentGreatConfig, base_equipment_great) ->
                    %% 进化达到上限了
                    {false, ?ERRCODE(err152_max_star)};
                CheckCostGoods =:= false ->
                    %% 材料出错了
                    {false, ?ERRCODE(err152_rise_star_stuff)};
                true ->
                    #base_equipment_great{cost = Cost, ratio = BaseRatio} = EquipmentGreatConfig,
                    {ExtraCostGoods, AddRatio} = CheckCostGoods,
                    PetAddRatio = lib_pet_api:pet_life_skill_about_equip_rise_star(Player),
                    WelfareAddRatio = lib_recharge_welfare:equip_rise_star_add(Player),
                    %% 最终进化概率
                    Ratio = BaseRatio + AddRatio + PetAddRatio * 100 + WelfareAddRatio * 100,
                    case MarketBuy of
                        [] ->
                            {true, GoodsInfo, ExtraCostGoods, Ratio, NextLv, 0, Cost, EquipmentGreatConfig};
                        _ ->
                            %% 判断市场商品在不在/且概率够不够100%
                            case check_market_buy(MarketBuy, Career, Series, NextLv, Ratio, RoleId) of
                                {true, NeedCoin} ->
                                    {true, GoodsInfo, ExtraCostGoods, 10000, NextLv, NeedCoin, [{?TYPE_COIN, 0, NeedCoin}|Cost], EquipmentGreatConfig};
                                false ->
                                    {false, ?ERRCODE(err152_market_no_goods)}
                            end
                    end
            end
    end.

%% 装备进化升星
equipment_rise_star(Player, GoodsId, Location, CostGoodsList, MarketBuy, AutoBuy) ->
    case equipment_rise_star_check(Player, GoodsId, Location, CostGoodsList, MarketBuy, AutoBuy) of
        {false, Err} -> {false, Err};
        {true, NewGoodsInfo, ExtraCostGoods, Ratio, NextLv, NeedCoin, Cost, EquipmentGreatConfig} ->
            Res =
                case AutoBuy of
                    1 ->
                        lib_goods_api:cost_objects_with_auto_buy(Player, Cost, equipment_star, integer_to_list(NeedCoin));
                    _ ->
                        lib_goods_api:cost_object_list_with_check(Player, Cost, equipment_star, "")
                end,
            case Res of
                {false, Err, _} -> {false, Err};
                {true, Player1, _} ->
                    #player_status{id = RoleId, figure = #figure{name = Name}} = Player,
                    MarketBuy =/= [] andalso market_mgr:cast({mgr_equipment_rise_star_buy, MarketBuy, RoleId, Name}),
                    %% 概率矫正，大于95%就认为是100%
                    FixRatio = case Ratio >= ?EQUIPMENT_STAR_RISE_RATIO of
                                   true  -> 10000;
                                   false -> Ratio
                               end,
                    Success = FixRatio >= urand:rand(1, 10000),
                    equipment_rise_star_do(Player1, NewGoodsInfo, ExtraCostGoods, Success, NextLv, EquipmentGreatConfig, FixRatio, Cost, AutoBuy)
            end
    end.

equipment_rise_star_do(Player, GoodsInfo, ExtraCostGoods, false, NextLv, EquipmentGreatConfig, FixRatio, Cost, AutoBuy) ->
    ExtraCostGoods =/= [] andalso lib_goods_api:delete_more_by_list(Player, ExtraCostGoods, equipment_star),
    log_equipment_great(Player, EquipmentGreatConfig, NextLv - 1, NextLv - 1, GoodsInfo, 0, FixRatio, ExtraCostGoods, Cost),
    ta_agent_fire:equip_great(Player, GoodsInfo, FixRatio, false, AutoBuy, ExtraCostGoods),
    {ok, Player1} = lib_seven_days_goal:do_finish_task(Player, ?MOD_EQUIP, 2),
    {ok, Player2} = lib_seven_days_goal:do_finish_task(Player1, ?MOD_EQUIP, 9, 1, true),
    Series = equipment_api:get_equipment_series(GoodsInfo),
    case Series == 2 of
        true -> {ok, Player3} = lib_seven_days_goal:do_finish_task(Player2, ?MOD_EQUIP, 10);
        _ -> Player3 = Player2
    end,
    {fail, Player3};
equipment_rise_star_do(Player, GoodsInfo, ExtraCostGoods, true, NextLv, EquipmentGreatConfig, FixRatio, Cost, AutoBuy) ->
    NewGoodsInfo = goods_extra_data:extra_data_update(GoodsInfo, [{?GOODS_OTHER_KEY_EQUIPMENT_GREAT_LV, NextLv}]),
    goods_logic:upgrade_goods(Player, NewGoodsInfo, ExtraCostGoods, equipment_star),
    {ok, Player1} = lib_player_event:dispatch(Player, ?EVENT_EQUIP, equipment_api:get_all_equipment()),
    Player2 = equipment_api:update_equipment_power(Player1),
    %% 成就
    RiseList = equipment_api:get_equipment_rise_info(),
    lib_achievement_api:equip_upgrade_event(Player, RiseList),
    custom_act_mobilize:update_mobilize_task(Player, equipgreat, 1),
    %% 任务
    StarNum = equipment_api:include_equipment_stars(),
    lib_task_api:rise_equip(Player, StarNum),
    ta_agent_fire:equip_great(Player, NewGoodsInfo, FixRatio, true, AutoBuy, ExtraCostGoods),
    lib_activitycalen_api:role_success_end_activity(Player2, ?MOD_EQUIP, 0),
    {ok, Player3} = lib_seven_days_goal:do_finish_task(Player2, ?MOD_EQUIP, 2),
    {ok, Player4}= lib_seven_days_goal:do_finish_task(Player3, ?MOD_EQUIP, 9, 1, true),
    Series = equipment_api:get_equipment_series(GoodsInfo),
    case Series == 2 of
        true -> {ok, Player5}= lib_seven_days_goal:do_finish_task(Player4, ?MOD_EQUIP, 10);
        _ -> Player5 = Player4
    end,
    {ok, Player6} = lib_seven_days_goal:do_finish_task(Player5, ?MOD_EQUIP, 0, StarNum, true),
    %% 传闻
    case NextLv >= 3 of
        true  ->
            #player_status{id = RoleId, figure = #figure{name = Name}} = Player,
            #goods{goods_id = GoodsTypeId, id = GoodsId} = NewGoodsInfo,
            lib_chat:send_TV({all}, ?MOD_EQUIP, ?LAN_TV_EQUIPMENT_RISE, [Name, RoleId, GoodsId, GoodsTypeId, NextLv]);
        false ->
            ok
    end,
    log_equipment_great(Player, EquipmentGreatConfig, NextLv - 1, NextLv, NewGoodsInfo, 1, FixRatio, ExtraCostGoods, Cost),
    {true, Player6, NextLv}.

%%---------------------------------------------------------------------
%% @doc 装备继承
%%---------------------------------------------------------------------
%% 穿装备的时候判断
%% 装备7件：继承强化等级、进化等级
%% 饰品3件：只继承强化等级
%% 守护1件：不参与继承
check_auto_inherit(_Player, #goods{subtype = SubType} = NewGoodsInfo, #goods{} = OldGoodsInfo)->
    %% 自动继承读装备配置的品质
    #base_equipment{color = ColorNew} = data_equipment:get_equipment_info(NewGoodsInfo#goods.goods_id),
    #base_equipment{color = ColorOld} = data_equipment:get_equipment_info(OldGoodsInfo#goods.goods_id),

    OldStrengthLv = equipment_api:get_equipment_strength_lv(OldGoodsInfo),
    OldGreatLv = equipment_api:get_equipment_great_lv(OldGoodsInfo),

    IsJewelry = lists:member(SubType, ?EQUIPMENT_JEWELRY),
    if
        ColorNew =< ColorOld ->
            %% 品质一样或者品质更低，不需要继承
            {NewGoodsInfo, OldGoodsInfo};
        SubType =:= ?EQUIPMENT_GUARD ->
            %% 守护不参与继承
            {NewGoodsInfo, OldGoodsInfo};
        IsJewelry andalso OldStrengthLv == 0 ->
            %% 首饰没有强化不需要继承
            {NewGoodsInfo, OldGoodsInfo};
        (not IsJewelry) andalso (OldStrengthLv == 0 andalso OldGreatLv =< 1) ->
            %% 武器和防具没有强化或者进化不需要继承
            {NewGoodsInfo, OldGoodsInfo};
        true ->
            case IsJewelry of
                true  ->
                    %% 首饰单独继承强化信息，旧装备删除对应信息
                    NewGoodsInfo1 =
                        case OldStrengthLv > 0 of
                            true  ->
                                goods_extra_data:extra_data_update(NewGoodsInfo, [{?GOODS_INFO_EQUIPMENT_LV, OldStrengthLv}]);
                            false ->
                                NewGoodsInfo
                        end,
                    Ratio = equipment_api:get_equipment_strength_success_ratio(OldGoodsInfo, 0),
                    NewGoodsInfo2 =
                        case Ratio > 0 of
                            true  ->
                                goods_extra_data:extra_data_update(NewGoodsInfo1, [{?GOODS_INFO_EQUIPMENT_SUCCESS_RATIO, Ratio}]);
                            false ->
                                NewGoodsInfo1
                        end,
                    NewGoodsInfo2,
                    OldGoodsInfo1 = goods_extra_data:extra_data_del(OldGoodsInfo, ?GOODS_INFO_EQUIPMENT_LV),
                    OldGoodsInfo2 = goods_extra_data:extra_data_del(OldGoodsInfo1, ?GOODS_INFO_EQUIPMENT_SUCCESS_RATIO),
                    {
                        NewGoodsInfo2, OldGoodsInfo2
                    };
                false ->
                    %% 普通装备全额继承所有信息，删除旧装备
                    %% 并且保留新装备的进化等级
                    NewGoodsInfo1 = NewGoodsInfo#goods{extra_data = OldGoodsInfo#goods.extra_data},
                    NewGreatLv = equipment_api:get_equipment_great_lv(NewGoodsInfo),
                    NewGoodsInfo2 =
                        case NewGreatLv > 0 andalso NewGreatLv > OldGreatLv of
                            true  ->
                                goods_extra_data:extra_data_update(NewGoodsInfo1, [{?GOODS_OTHER_KEY_EQUIPMENT_GREAT_LV, NewGreatLv}]);
                            false ->
                                NewGoodsInfo1
                        end,
                    {
                        NewGoodsInfo2, OldGoodsInfo#goods{num = 0}
                    }
            end
    end;
check_auto_inherit(_Player, NewGoodsInfo, OldGoodsInfo) -> {NewGoodsInfo, OldGoodsInfo}.

%%---------------------------------------------------------------------
%% @doc 判断星星总数是否增加 穿装备/升星
%%---------------------------------------------------------------------
equipment_active_star(Player, Choice) ->
    #player_status{equipment = Equipment, id = RoleId, figure = #figure{name = Name} = Figure} = Player,
    ActiveStars = Equipment#equipment.stars,
    NowStars = equipment_api:include_equipment_stars(),
    NewStars = get_next_stars(ActiveStars),
    AllBranches = data_equipment_check:get_equipment_stars_branch(NewStars),
    IsChoice =
        case is_list(AllBranches) of
            true  ->
                lists:keyfind(Choice, 1, AllBranches);
            false ->
                false
        end,
    if
        NowStars < NewStars ->
            {false, ?ERRCODE(err152_err_condition)};
        Choice =:= 0 andalso AllBranches =/= [] ->
            {false, ?ERRCODE(err152_no_choice)};
        Choice > 0 andalso IsChoice == false ->
            {false, ?ERRCODE(err152_choice_not_needed)};
        true ->
            StarsInfo = Equipment#equipment.stars_info,
            NewStarsInfo =
            case Choice > 0 of
                true  ->
                    [{NewStars, Choice}|StarsInfo];
                false ->
                    StarsInfo
            end,
            NewEquipment = Equipment#equipment{stars = NewStars, stars_info = NewStarsInfo},
            save_equipment_info(RoleId, NewEquipment),
            Player1 = Player#player_status{equipment = NewEquipment},
            Player2 = equipment_api:update_equipment_power(Player1),
            lib_common_rank_api:refresh_equip_stars(Player),
            lib_log_api:log_equipment_star(
                [RoleId, Name, Figure#figure.lv, NowStars, NewStars, Choice, utime:unixtime()]
            ),
            %% 传闻
            case NewStars > 0 andalso NewStars rem 5 =:= 0 of
                true  ->
                    lib_chat:send_TV({all}, ?MOD_EQUIP, ?LAN_TV_EQUIPMENT_STAR, [Name, NewStars]);
                false ->
                    ok
            end,
            {ok, Player2, NewStars}
    end.

%% 计算下一个激活节点
get_next_stars(ActiveStars) ->
    Max = data_equipment_check:get_max_stars(),
    get_next_stars(ActiveStars + 1, Max).
get_next_stars(Stars, Max) when Stars > Max -> false;
get_next_stars(Stars, Max) ->
    case data_equipment_check:get_equipment_stars_branch(Stars) of
        Choice when is_list(Choice) ->
            Stars;
        _ ->
            get_next_stars(Stars + 1, Max)
    end.

%%---------------------------------------------------------------------
%% @doc 解锁装备代数
%%---------------------------------------------------------------------
equipment_unlock_series(Player) ->
    #player_status{equipment = Equipment, figure = Figure, id = RoleId} = Player,
    #equipment{unlock_series = UnlockSeries} = Equipment,
    NextUnlock = UnlockSeries + 1,
    case data_equipment:get_equipment_series_open(NextUnlock) of
        {NeedLv, TaskId} ->
            IsFinish = TaskId =:= 0 orelse lib_role_task:is_finish_task_id(TaskId),
            if
                Figure#figure.lv < NeedLv -> {false, ?ERRCODE(err152_unlock_lv_limit)};
                not IsFinish -> {false, ?ERRCODE(err152_series_task)};
                true ->
                    NewEquipment = Equipment#equipment{unlock_series = NextUnlock},
                    save_equipment_info(RoleId, NewEquipment),
                    Player1 = Player#player_status{equipment = NewEquipment},
                    {ok, Player1, NextUnlock}
            end;
        _ ->
            {false, ?MISSING_CONFIG}
    end.
%%---------------------------------------------------------------------
%% @doc 器灵升级
%%---------------------------------------------------------------------
equipment_empower_check(Player, Series, Pos) ->
    Lv = equipment_api:get_equipment_empower(Player, Series, Pos),
    EmpowerConfig = data_equipment:get_equipment_empower(Series, Pos, Lv+1),
    GoodsInfo = equipment_api:get_series_pos_equipment(Series, Pos),
    if
        not is_record(GoodsInfo, goods) -> {false, ?ERRCODE(err152_pos_no_equip)};
        Pos =:= ?EQUIPMENT_GUARD -> {false, ?ERRCODE(err152_cannot_empower)};
        not is_record(EmpowerConfig, base_equipment_empower) -> {false, ?ERRCODE(err152_empower_max_lv)};
        true ->
            #base_equipment_empower{condition = Condition} = EmpowerConfig,
            case Condition of
                {equip_color, NeedColor} ->
                    %% 一般是橙色才能升级
                    case GoodsInfo#goods.color >= NeedColor of
                        true  ->
                            {true, EmpowerConfig, Lv+1};
                        false ->
                            {false, ?ERRCODE(err152_cannot_empower)}
                    end;
                {all_series_lv, AllSeries, NeedLv} ->
                    %% 一般需要前N代装备全部达到一定等级才能继续升级
                    case check_previous_empower(Player, AllSeries, NeedLv) of
                        true  ->
                            {true, EmpowerConfig, Lv+1};
                        false ->
                            {false, ?ERRCODE(err152_empower_conditon)}
                    end
            end
    end.

%% 器灵升级
equipment_empower(Player, Series, Pos) ->
    case equipment_empower_check(Player, Series, Pos) of
        {false, Err} -> {false, Err};
        {true, EmpowerConfig, Lv} ->
            case lib_goods_api:cost_object_list_with_check(Player, EmpowerConfig#base_equipment_empower.cost, equipment_empower, "") of
                {false, Err, _} -> {false, Err};
                {true, Player1, _} ->
                    equipment_empower_do(Player1, EmpowerConfig, Lv)
            end
    end.

equipment_empower_do(Player, EmpowerConfig, NextLv) ->
    #player_status{id = RoleId, figure = #figure{name = Name}} = Player,
    #base_equipment_empower{series = Series, pos = Pos} = EmpowerConfig,
    Player1 = empower_data_save(Player, Series, Pos, NextLv),
    NewEquipmentSeries = Player1#player_status.equipment#equipment.equipment_series,
    %% 触发成就
    EmpowerData = equipment_api:get_equipment_empower_info(NewEquipmentSeries),
    lib_achievement_api:wear_red_equip_event(Player1, EmpowerData),
    %% 七日目标
    Count = lists:foldl(fun(Empower, TSum) ->
                        case Empower of
                            {_, Num} -> TSum + Num;
                            _ -> TSum
                        end end, 0, EmpowerData),
    {ok, Player2} = lib_seven_days_goal:do_finish_task(Player1, ?MOD_EQUIP, 4, Count),
    %% 精英狩猎
    EmPowers = equipment_api:get_equipment_empower_info(NewEquipmentSeries),
    %% 任务
    lib_task_api:empower_equip(Player2, EmPowers),
    %%
    ta_agent_fire:equip_empower(Player, EmpowerConfig, NextLv),
    Player3 = equipment_api:set_equip_empower_attr(Player2),
    Player4 = equipment_api:update_equipment_power(Player3),
    lib_log_api:log_equipment_empower(RoleId, Name,Series, Pos, NextLv-1, NextLv, EmpowerConfig#base_equipment_empower.cost),
    {ok, Player4, NextLv}.

empower_data_save(Player, Series, Pos, NextLv) ->
    #player_status{equipment = StatusEquipment, id = RoleId} = Player,
    #equipment{equipment_series = EquipmentSeries} = StatusEquipment,
    SeriesInfo = equipment_api:get_equipment_series_pos_info(EquipmentSeries, Series, Pos),
    NewSeriesInfo = SeriesInfo#equipment_series{empower = NextLv},
    save_equipment_series_pos_info(RoleId, NewSeriesInfo),
    NewEquipmentSeries = equipment_api:set_equipment_series_pos_info(EquipmentSeries, NewSeriesInfo),
    NewStatusEquipment = StatusEquipment#equipment{equipment_series = NewEquipmentSeries},
    Player#player_status{equipment = NewStatusEquipment}.

%% 前Series代装备器灵都达到NeedLv
check_previous_empower(_Player, 0, _NeedLv) -> true;
check_previous_empower(Player, Series, NeedLv) ->
    #player_status{equipment = #equipment{equipment_series = EquipmentSeries}} = Player,
    case check_previous_empower_by_pos(
        ?EQUIPMENT_NORMAL,
        maps:get(Series, EquipmentSeries, #{}),
        NeedLv
    )
    of
        true  -> check_previous_empower(Player, Series - 1, NeedLv);
        false -> false
    end.

check_previous_empower_by_pos([], _PosInfo, _NeedLv) -> true;
check_previous_empower_by_pos([Pos|T], PosInfo, NeedLv) ->
    #equipment_series{empower = Lv} = maps:get(Pos, PosInfo, #equipment_series{}),
    case Lv >= NeedLv of
        true  ->
            check_previous_empower_by_pos(T, PosInfo, NeedLv);
        false ->
            false
    end.
%%---------------------------------------------------------------------
%% @doc 进化预览:同职业，优先拿同一套数，不足优先拿套数高的，同套优先拿品质低的
%%---------------------------------------------------------------------
rise_star_preview(Player, GoodsId, Lv, NowRatio) ->
    case data_equipment:get_equipment_info(GoodsId) of
        #base_equipment{series = Series} ->
            #player_status{id = RoleId, figure = #figure{career = Career}} = Player,
            MarketGoodsList = select_goods_list(RoleId, Career),
            SortGoodsList = sort_goods_list(MarketGoodsList),
            F1 = fun(#market_goods{series = GoodsSeries}) -> GoodsSeries =:= Series end,
            {SelfSeriesList, OtherSeriesList} = lists:partition(F1, SortGoodsList),
            GoodsList = SelfSeriesList ++ OtherSeriesList,
            case calc_goods_list(GoodsList, Series, Lv, NowRatio) of
                List when is_list(List) ->
                    {true, List};
                _ ->
                    {false, ?ERRCODE(err152_market_no_goods)}
            end;
        _ ->
            {false, ?ERRCODE(err152_not_exist)}
    end.
%% 找出符合要求的装备
select_goods_list(RoleId, Career) ->
    Now = utime:unixtime(),
    F = fun(MarketGoods, Temp) ->
            case check_can_buy(MarketGoods, Career, RoleId, Now) of
                true  ->
                    [MarketGoods|Temp];
                false ->
                    Temp
            end
        end,
    ets:foldl(F, [], ?MARKET_GOODS_ETS).

%% 装备是否可以购买
check_can_buy(MarketGoods, Career, RoleId, Now) ->
    #market_goods{
        id = MarketId,
        role_id = Seller,
        off_time = OffTime,
        career = Careers,
        color = Color,
        sell_type = SellType
    } = MarketGoods,
    ShowTime = market:rest_show_time(MarketGoods, Now),
    IsBid = ets:lookup(?MARKET_BID_ETS, {RoleId, MarketId}) =/= [],
    IsMyCareer = lists:member(Career, Careers),
    IsMyCareer andalso Seller =/= RoleId
        andalso Color =:= ?PURPLE
        andalso OffTime > Now
        andalso SellType =:= ?MARKET_SELL_TYPE_WORLD
        andalso ShowTime =< 0
        andalso not IsBid.

%% 排序
sort_goods_list(MarketGoodsList) ->
    F = fun(
        #market_goods{series = SeriesA, color = ColorA},
        #market_goods{series = SeriesB, color = ColorB}
    ) ->
        if
            SeriesA > SeriesB -> true;
            SeriesA < SeriesB -> false;
            true -> ColorA < ColorB
        end
        end,
    lists:sort(F, MarketGoodsList).
%% 概率够了就行了
calc_goods_list(MarketGoodsList, Series, Lv, NowRatio) ->
    calc_goods_list(MarketGoodsList, Series, Lv, NowRatio, []).
calc_goods_list(_MarketGoodsList, _Series, _Lv, NowRatio, List) when NowRatio >= ?EQUIPMENT_STAR_RISE_RATIO ->
    List;
calc_goods_list([H|T], Series, Lv, NowRatio, List) ->
    #market_goods{
        id = MarketId,
        goods_type_id = GoodsId,
        series = GoodsSeries,
        buyout_price = Price,
        color = Color
    } = H,
    case data_equipment:get_great_add_ratio(GoodsSeries, Color, Series, Lv) of
        [AddRatio] when AddRatio > 0 ->
            NewRatio = NowRatio + AddRatio,
            NewList = [{MarketId, GoodsId, Price}|List],
            calc_goods_list(T, Series, Lv, NewRatio, NewList);
        _ ->
            calc_goods_list(T, Series, Lv, NowRatio, List)
    end;
calc_goods_list([], _Series, _Lv, _NowRatio, _List) ->
    false.

check_market_buy(MarketIds, Career, Series, Lv, NowRatio, RoleId) ->
    Now = utime:unixtime(),
    check_market_buy_do(MarketIds, Career, Series, Lv, NowRatio, RoleId, Now, 0).
check_market_buy_do([], _Career, _Series, _Lv, Ratio, _RoleId, _Now, Price) ->
    case Ratio >= ?EQUIPMENT_STAR_RISE_RATIO of
        true  ->
            {true, Price};
        false ->
            false
    end;
check_market_buy_do([Id|T], Career, Series, Lv, Ratio, RoleId, Now, TotalPrice) ->
    case ets:lookup(?MARKET_GOODS_ETS, Id) of
        [MarketGoods] ->
            case check_can_buy(MarketGoods, Career, RoleId, Now) of
                true  ->
                    #market_goods{
                        series = GoodsSeries,
                        color = Color,
                        buyout_price = Price
                    } = MarketGoods,
                    case data_equipment:get_great_add_ratio(GoodsSeries, Color, Series, Lv) of
                        [AddRatio] when AddRatio > 0 ->
                            check_market_buy_do(T, Career, Series, Lv, AddRatio+Ratio, RoleId, Now, Price + TotalPrice);
                        _ ->
                            false
                    end;
                false ->
                    false
            end;
        _ ->
            false
    end.

%%---------------------------------------------------------------------
%% @doc 宝石操作
%%---------------------------------------------------------------------
equipment_gems_inlay_check(Player, Series, Pos, GoodsId) ->
    #player_status{equipment = #equipment{equipment_series = EquipmentSeries}} = Player,
    GemCfg = data_equipment:get_equipment_gem_info(GoodsId),
    GemInfo = equipment_api:get_equipment_series_pos_info(EquipmentSeries, Series, Pos),
    IsEquip = equipment_api:is_series_pos_equip(Series, Pos),
    if
        not IsEquip -> {false, ?ERRCODE(err152_pos_no_equip)};
        not is_record(GemCfg, base_equipment_gems) -> {false, 2};
        true ->
            #base_equipment_gems{type = Type} = GemCfg,
            case lists:keyfind(Type, 1, GemInfo#equipment_series.gems) of
                {_, OldGemId} ->
                    case lib_goods_api:can_give_goods(Player, [{?TYPE_BIND_GOODS, OldGemId, 1}]) of
                        true  ->
                            {true, Type, OldGemId};
                        false ->
                            {false, ?ERRCODE(err150_no_cell)}
                    end;
                _ ->
                    {true, Type, 0}
            end
    end.

%% @doc 宝石镶嵌
equipment_gems_inlay(Player, Series, Pos, GoodsId) ->
    case equipment_gems_inlay_check(Player, Series, Pos, GoodsId) of
        {false, Err} -> {false, Err};
        {true, Type, OldGemId} ->
            case lib_goods_api:cost_object_list_with_check(Player, [{?TYPE_GOODS, GoodsId, 1}], equipment_gems_inlay, "") of
                {false, Err, _} -> {false, Err};
                {true, Player1, _} ->
                    equipment_gems_inlay_new(Player1, Series, Pos, Type, GoodsId, OldGemId, ?EQUIPMENT_GEM_OP_INLAY)
            end
    end.

%% 镶嵌成新的宝石：镶嵌新的和升级均走这里
%% OpType:1镶嵌|2摘除|3升级
equipment_gems_inlay_new(Player, Series, Pos, Type, GemId, OldGemId, OpType) ->
    #player_status{equipment = StatusEquipment, id = RoleId, figure = #figure{name = Name}} = Player,
    #equipment{equipment_series = EquipmentSeries} = StatusEquipment,
    GemInfo = equipment_api:get_equipment_series_pos_info(EquipmentSeries, Series, Pos),
    NewGemInfo = GemInfo#equipment_series{
        gems = lists:keystore(Type, 1, GemInfo#equipment_series.gems, {Type, GemId})
    },
    save_equipment_series_pos_info(RoleId, NewGemInfo),
    NewEquipmentSeries = equipment_api:set_equipment_series_pos_info(EquipmentSeries, NewGemInfo),
    NewStatusEquipment = StatusEquipment#equipment{equipment_series = NewEquipmentSeries},
    Player1 = Player#player_status{equipment = NewStatusEquipment},
    Player2 = equipment_api:update_equipment_power(Player1),
    Player3 = deal_with_old_gem(Player2, OldGemId, OpType),
    lib_log_api:log_equipment_gems(RoleId, Name, OpType, Series, Pos, OldGemId, GemId),
    OldSumLv = calc_sum_lv(EquipmentSeries),
    NewSumLv = calc_sum_lv(NewEquipmentSeries),
    (OpType =:= 3 orelse OpType =:= 1) andalso
    custom_act_mobilize:update_mobilize_gem_task(Player2, OldSumLv, NewSumLv),
    after_upgrade_gems(Player3, NewEquipmentSeries),
    {true, Player3}.

%% 宝石变更后
after_upgrade_gems(Player, NewEquipmentSeries) ->
    %% 触发成就
    lib_achievement_api:wear_equip_stone_event(Player, equipment_api:get_equipment_gems_info(NewEquipmentSeries)),
    ok.

calc_sum_lv(NewEquipmentSeries) ->
    LvList = equipment_api:get_equipment_gems_info(NewEquipmentSeries),
    F = fun(Info, SumLv) ->
        case Info of
            {Lv, Num} -> Lv * Num + SumLv;
            _ -> SumLv
        end end,
    SumLv = lists:foldl(F, 0, LvList),
    SumLv.

deal_with_old_gem(Player, OldGemId, ?EQUIPMENT_GEM_OP_INLAY) when OldGemId > 0 ->
    lib_goods_api:send_reward(Player, [{?TYPE_BIND_GOODS, OldGemId, 1}], equipment_gems_unload);
deal_with_old_gem(Player, _, _) -> Player.

%% @doc 宝石卸下
equipment_gems_unload(Player, Series, Pos, GoodsId) ->
    #player_status{equipment = StatusEquipment, id = RoleId, figure = #figure{name = Name}} = Player,
    #equipment{equipment_series = EquipmentSeries} = StatusEquipment,
    GemInfo = equipment_api:get_equipment_series_pos_info(EquipmentSeries, Series, Pos),
    IsInlay = lists:keymember(GoodsId, 2, GemInfo#equipment_series.gems),
    CanGive = lib_goods_api:can_give_goods(Player, [{?TYPE_BIND_GOODS, GoodsId, 1}]),
    if
        not IsInlay -> {false, ?ERRCODE(err152_no_inlay_gem)};
        not CanGive -> {false, ?ERRCODE(err150_no_cell)};
        true  ->
            NewGemInfo = GemInfo#equipment_series{
                gems = lists:keydelete(GoodsId, 2, GemInfo#equipment_series.gems)
            },
            save_equipment_series_pos_info(RoleId, NewGemInfo),
            NewEquipmentSeries = equipment_api:set_equipment_series_pos_info(EquipmentSeries, NewGemInfo),
            NewStatusEquipment = StatusEquipment#equipment{equipment_series = NewEquipmentSeries},
            Player1 = Player#player_status{equipment = NewStatusEquipment},
            Player2 = lib_goods_api:send_reward(Player1, [{?TYPE_BIND_GOODS, GoodsId, 1}], equipment_gems_unload),
            Player3 = equipment_api:update_equipment_power(Player2),
            lib_log_api:log_equipment_gems(RoleId, Name, ?EQUIPMENT_GEM_OP_UNLOAD, Series, Pos, GoodsId, 0),
            {true, Player3}
    end.

%% @doc 宝石升级-对于已镶嵌的
equipment_gems_upgrade_check(Player, Series, Pos, GoodsId) ->
    #player_status{equipment = StatusEquipment} = Player,
    #equipment{equipment_series = EquipmentSeries} = StatusEquipment,
    GemInfo = equipment_api:get_equipment_series_pos_info(EquipmentSeries, Series, Pos),
    IsInlay = lists:keymember(GoodsId, 2, GemInfo#equipment_series.gems),
    GemCfg = data_equipment:get_equipment_gem_info(GoodsId),
    if
        not IsInlay -> {false, ?ERRCODE(err152_no_inlay_gem)};
        not is_record(GemCfg, base_equipment_gems) -> {false, ?MISSING_CONFIG};
        true  ->
            #base_equipment_gems{type = Type, lv = Lv} = GemCfg,
            NextLvGem = data_equipment:get_equipment_gem_by_lv(Type, Lv+1),
            if
                NextLvGem =:= 0 -> {false, ?ERRCODE(err152_gem_max_lv)};
                true ->
                    %% 循环找到可以用来消耗升级的宝石
                    case circle_get_enough_gems(Player, Type, 2, Lv, []) of
                        false -> {false, ?GOODS_NOT_ENOUGH};
                        Cost ->  {true, Type, NextLvGem, Cost}
                    end
            end
    end.
circle_get_enough_gems(_Player, _Type, _NeedNum, 0, _Cost) -> false;
circle_get_enough_gems(Player, Type, NeedNum, Lv, Cost) ->
    GoodsId = data_equipment:get_equipment_gem_by_lv(Type, Lv),
    [{_, Have}] = lib_goods_api:get_goods_num(Player, [GoodsId]),
    case Have >= NeedNum of
        true  ->
            [{?TYPE_GOODS, GoodsId, NeedNum}|Cost];
        false ->
            NewNeedNum = (NeedNum - Have) * 3,
            NewCost = case Have =:= 0 of
                true  -> Cost;
                false -> [{?TYPE_GOODS, GoodsId, Have}|Cost]
            end,
            circle_get_enough_gems(Player, Type, NewNeedNum, Lv-1, NewCost)
    end.
equipment_gems_upgrade(Player, Series, Pos, GoodsId) ->
    case equipment_gems_upgrade_check(Player, Series, Pos, GoodsId) of
        {false, Err} -> {false, Err};
        {true, Type, NextLvGem, Cost} ->
            case lib_goods_api:cost_object_list_with_check(Player, Cost, equipment_gems_upgrade, "") of
                {false, Err, _} -> {false, Err};
                {true, Player1, _} ->
                    equipment_gems_inlay_new(Player1, Series, Pos, Type, NextLvGem, GoodsId, ?EQUIPMENT_GEM_OP_UPGRADE)
            end
    end.

%% @doc 激活宝石套装
equipment_gems_active_suit(Player, Series) ->
    #player_status{equipment = StatusEquipment, id = RoleId} = Player,
    #equipment{gems_suit = GemsSuit} = StatusEquipment,
    GemsLv = equipment_api:get_equipment_cell_gems_lv(StatusEquipment, Series),
    NowActive =
    case lists:keyfind(Series, 1, GemsSuit) of
        {_, _NowActive} -> _NowActive;
        _ -> 0
    end,
    case get_next_lv(NowActive, data_equipment:get_series_all_gems_lv(Series)) of
        false -> {false, ?ERRCODE(err152_gem_suit_max)};
        NextLv when NextLv > GemsLv -> {false, ?ERRCODE(err152_collect_condition)};
        NextLv ->
            StatusEquipment1 = StatusEquipment#equipment{gems_suit = lists:keystore(Series, 1, GemsSuit, {Series, NextLv})},
            save_equipment_info(RoleId, StatusEquipment1),
            Player1 = Player#player_status{equipment = StatusEquipment1},
            Player2 = equipment_api:update_equipment_power(Player1),
            {true, Player2, NextLv}
    end.

get_next_lv(NowLv, [Lv|_]) when Lv > NowLv -> Lv;
get_next_lv(NowLv, [_|T]) -> get_next_lv(NowLv, T);
get_next_lv(_NowLv, []) -> false.
%%----------lsl-----------------------------------------------------------
%% @doc 宝石一键镶嵌
%% 实现效果:
%% 1将背包中的宝石，按照等级从高到低，依次填入身上可用的空宝石孔
%% 2在这之后，从玩家身上最低等级的宝石开始，遍历替换操作，直到背包中没有可替换高等级的宝石
%% 3在这之后，从玩家身上最低等级的宝石开始，遍历合成操作，直到全部宝石都不可再合成
%%---------------------------------------------------------------------
%% 中间数据
-record(gems_info_temp, {
    pos      = 0,       %% 装备位置/如果在背包里则为0
    goods_id = 0,       %% 宝石id
    old_gem  = 0,       %% 初始的宝石id/穿戴使用，日志记录用
    type     = 0,       %% 宝石类型
    lv       = 0,       %% 宝石等级
    num      = 0,       %% 数量
    update   = false    %% 更新标识
}).
%% 为了逻辑清晰，将宝石按类型区分，每一种类型的处理逻辑是一样的，这样，每次处理都是只处理一种类型的宝石
%% 最后再综合数据，统一结算
auto_set_gems(Player, Series) ->
    #player_status{equipment = StatusEquipment, id = RoleId, figure = #figure{name = Name}} = Player,
    #equipment{equipment_series = EquipmentSeries} = StatusEquipment,
    %% UpgradeWear :一键处理后每个槽位的情况
    %% NewCostGems :一键处理后需要消耗的宝石
    %% NewAddGems  :一键处理后替换下来的宝石
    {UpgradeWear, NewCostGems, NewAddGems} =
    lists:foldl(
        fun(GemType, {WearGemsTemp, AddGemsTemp, CostGemsTemp}) ->
            {TypeUpgradeWear, TypeAddGems, TypeCostGems} =
                auto_set_gems_do(EquipmentSeries, Series, GemType),
            {
                    TypeUpgradeWear ++ WearGemsTemp,
                    TypeAddGems ++ AddGemsTemp,
                    TypeCostGems ++ CostGemsTemp
            }
        end, {[], [], []}, [?GEM_TYPE_ATT, ?GEM_TYPE_DEF, ?GEM_TYPE_HP]
    ),
    case NewCostGems of
        [] ->
            %% 如果宝石消耗为空，说明没有宝石发生变更
            {false, ?ERRCODE(err152_has_no_gems)};
        _ ->
            %% 1>扣除宝石
            case lib_goods_api:cost_object_list_with_check(Player, NewCostGems, equipment_gems_inlay, "") of
                {false, Err, _} -> {false, Err};
                {true, Player1, _} ->
                    %% 将分散的宝石数据按位置综合起来
                    SumWear =
                        lists:foldl(
                            fun(One, Temp) ->
                                #gems_info_temp{type = GemType, pos = Pos, goods_id = GemId, update = Update, old_gem = OldGemId} = One,
                                %% 既然已经扣了宝石了，直接在这记日志也没事
                                Update andalso lib_log_api:log_equipment_gems(RoleId, Name, ?EQUIPMENT_GEM_OP_AUTO, Series, Pos, OldGemId, GemId),
                                case GemId > 0 of
                                    true  ->
                                        case lists:keyfind(Pos, 1, Temp) of
                                            {_, Bool, Gems} ->
                                                lists:keyreplace(Pos, 1, Temp, {Pos, Bool orelse Update, [{GemType, GemId}|Gems]});
                                            _ ->
                                                [{Pos, Update, [{GemType, GemId}]}|Temp]
                                        end;
                                    false ->
                                        Temp
                                end
                            end, [], UpgradeWear
                        ),
                    %% 2>将槽位数据回写到玩家身上
                    InlayGemInfo = maps:get(Series, EquipmentSeries, #{}),
                    {NewInlayGemInfo, SaveData} =
                        lists:foldl(
                            fun
                                ({Pos, true, GemsInfo}, {Temp, TempUpdate}) ->
                                    PosGems = maps:get(Pos, Temp, #equipment_series{series = Series, pos = Pos}),
                                    NewPosGems = PosGems#equipment_series{gems = GemsInfo},
                                    {
                                        maps:put(Pos, NewPosGems, Temp),
                                        [NewPosGems|TempUpdate]
                                    };
                                (_, {Temp, TempUpdate}) ->
                                    {Temp, TempUpdate}
                            end, {InlayGemInfo, []}, SumWear
                        ),
                    %% 3>批量保存更新了的宝石数据
                    batch_equipment_series_pos_info(RoleId, SaveData),
                    NewEquipmentSeries = maps:put(Series, NewInlayGemInfo, EquipmentSeries),
                    NewStatusEquipment = StatusEquipment#equipment{equipment_series = NewEquipmentSeries},
                    Player2 = Player1#player_status{equipment = NewStatusEquipment},
                    %% 4>返还替换下来的宝石
                    Player3 =
                    case NewAddGems =/= [] of
                        true ->
                            lib_goods_api:send_reward(Player2, NewAddGems, equipment_gems_unload);
                        false  ->
                            Player2
                    end,
                    Player4 = equipment_api:update_equipment_power(Player3),
                    OldSumLv = calc_sum_lv(EquipmentSeries),
                    NewSumLv = calc_sum_lv(NewEquipmentSeries),
                    custom_act_mobilize:update_mobilize_gem_task(Player4, OldSumLv, NewSumLv),
                    after_upgrade_gems(Player4, NewEquipmentSeries),
                    {true, Player4, [ChangePos || #equipment_series{pos  = ChangePos} <- SaveData]}
            end
    end.

%% 按宝石类型一键操作
auto_set_gems_do(EquipmentSeries, Series, Type) ->
    %% 将身上穿戴的和背包里的宝石转为中间数据，便于计算
    %% 1>统计背包里的所有宝石, 构造成#gems_info_temp{}格式，每种宝石一条数据
    GoodsStatus = lib_goods_do:get_goods_status(),
    AllGemsList = dict:fold(
        fun(_Key, #goods{type = GoodsType, subtype = SubType, goods_id = GoodsTypeId, num = Num}, Temp) ->
            case GoodsType =:= ?GOODS_TYPE_STUFF
                andalso SubType =:= ?GOODS_STUFF_SUBTYPE_GEMS of
                true  ->
                    case lists:keyfind(GoodsTypeId, #gems_info_temp.goods_id, Temp) of
                        #gems_info_temp{num = OldNum} = Info->
                            lists:keyreplace(GoodsTypeId, #gems_info_temp.goods_id, Temp, Info#gems_info_temp{num = OldNum + Num});
                        false ->
                            case data_equipment:get_equipment_gem_info(GoodsTypeId) of
                                #base_equipment_gems{type = Type, lv = Lv} ->
                                    Info = #gems_info_temp{type = Type, lv = Lv,num = Num, goods_id = GoodsTypeId},
                                    [Info|Temp];
                                _ ->
                                    Temp
                            end
                    end;
                false ->
                    Temp
            end
        end, [], lib_goods_util:get_location_goods(GoodsStatus#goods_status.dict, ?GOODS_LOC_BAG)
    ),
    %% 背包里的降序排列
    BagGems = lists:reverse(lists:keysort(#gems_info_temp.lv, AllGemsList)),
    %% 2>将已穿戴装备的宝石槽位初始化， 构造成#gems_info_temp{}格式，每个位置的每个槽位一条数据
    CanInlayPos     = ?EQUIPMENT_JEWELRY ++ ?EQUIPMENT_NORMAL,
    InlayGemInfo    = maps:get(Series, EquipmentSeries, #{}),
    EquipmentList   = equipment_api:get_all_equipment_list(GoodsStatus),
    AllWearList =
    lists:foldl(
        fun(Pos, Temp) ->
            IsEquip = equipment_api:is_series_pos_equip(EquipmentList, Series, Pos),
            case IsEquip of
                true  ->
                    #equipment_series{gems = Gems}
                        = maps:get(Pos, InlayGemInfo, #equipment_series{}),
                    [get_pos_type_gem(Pos, Type, Gems)] ++ Temp;
                false ->
                    Temp
            end
        end, [], CanInlayPos
    ),
    %% 身上的升序排列
    WearGems = lists:keysort(#gems_info_temp.lv, AllWearList),
    %% 第一步：将身上的槽位依次判断，如果是空的，就将背包里的第一个宝石镶嵌进去，如果是有宝石的，就判断背包里的第一个宝石是否可以替换
    %% 如果既不能镶嵌也不能替换则操作停止
    {InlayWear, LeftBag, AddGems, CostGems} =
        inlay_or_replace(WearGems, BagGems, [], [], []),
    %% 将替换下来的宝石，增加到背包里去
    NewBagGems = lists:foldl(
        fun(#gems_info_temp{goods_id = GoodsTypeId, num = Num} = One, Temp) ->
            case lists:keyfind(GoodsTypeId, #gems_info_temp.goods_id, Temp) of
                #gems_info_temp{num = OldNum} = Info->
                    lists:keyreplace(GoodsTypeId, #gems_info_temp.goods_id, Temp, Info#gems_info_temp{num = OldNum + Num});
                false ->
                    [One|Temp]
            end
        end, LeftBag, AddGems
    ),
    %% 第二步，每次找到身上等级最低的宝石，将其升一级，然后循环，直到不可升级为止
    {UpgradeWear, UpgradeCost} = auto_upgrade(InlayWear, NewBagGems, []),
    TotalCostGems = lib_goods_util:merge_same_goods(UpgradeCost ++ CostGems),
    %% 第三步，如果新增和扣除的列表里有相同的宝石，那就可以相互抵消
    {NewCostGems, NewAddGems} =
        lists:foldl(
            fun(#gems_info_temp{goods_id = GemId, num = Num}, {CostTemp, AddTemp}) ->
                case lists:keyfind(GemId, 1, CostTemp) of
                    {_, CostNum} when CostNum =:= Num ->
                        %% 增加的宝石和需要扣除的宝石一样多，那就直接抵消，不增不减
                        {
                            lists:keydelete(GemId, 1, CostTemp),
                            AddTemp
                        };
                    {_, CostNum} when CostNum > Num ->
                        %% 扣除的宝石较多，那就不需要新增宝石
                        {
                            lists:keyreplace(GemId, 1, CostTemp, {GemId, CostNum - Num}),
                            AddTemp
                        };
                    {_, CostNum} when CostNum < Num ->
                        %% 增加的宝石较多，那就不需要扣除宝石
                        {
                            lists:keydelete(GemId, 1, CostTemp),
                            [{?TYPE_BIND_GOODS, GemId, Num - CostNum}|AddTemp]
                        };
                    _ ->
                        %% 没有相同的宝石，那就全部新增宝石
                        {
                            CostTemp,
                            [{?TYPE_BIND_GOODS, GemId, Num}|AddTemp]
                        }
                end
            end, {TotalCostGems, []}, AddGems
        ),
    {UpgradeWear, NewCostGems, NewAddGems}.

%% 遍历身上的宝石：镶嵌或者替换
inlay_or_replace([], [], AfterCheck, ReplaceGems, CostGems) ->
    {AfterCheck, [], ReplaceGems, CostGems};
inlay_or_replace(WearGems, [], AfterCheck, ReplaceGems, CostGems) ->
    {AfterCheck ++ WearGems, [],  ReplaceGems, CostGems};
inlay_or_replace([], BagGems, AfterCheck, ReplaceGems, CostGems) ->
    {AfterCheck, BagGems, ReplaceGems, CostGems};
inlay_or_replace([WearGem|T], BagGems, AfterCheck, ReplaceGems, CostGems) ->
    #gems_info_temp{lv = Lv, goods_id = GemId} = WearGem,
    %% 判断背包里是否有大于Lv的宝石
    case check_gem_to_inlay(Lv, BagGems) of
        false ->
            %% 因为背包的宝石是降序排列的，第一个都不能镶嵌上去，那后面的就更加不用检查了
            {AfterCheck ++ [WearGem|T], BagGems, ReplaceGems, CostGems};
        {InlayGem, InlayLv, NewBagGems} ->
            NewReplaceGems =
                case GemId > 0 of
                    true  ->
                        %% 如果位置已经有数据了，那么替换，记录换下来的宝石
                        case lists:keyfind(GemId, #gems_info_temp.goods_id, ReplaceGems) of
                            #gems_info_temp{num = OldNum} = One ->
                                lists:keyreplace(GemId, #gems_info_temp.goods_id, ReplaceGems, One#gems_info_temp{num = OldNum+1});
                            _ ->
                                [WearGem#gems_info_temp{num = 1}|ReplaceGems]
                        end;
                    false ->
                        ReplaceGems
                end,
            NewWear = WearGem#gems_info_temp{goods_id = InlayGem, update = true, lv = InlayLv},
            inlay_or_replace(T, NewBagGems, [NewWear|AfterCheck], NewReplaceGems, [{InlayGem, 1}|CostGems])
    end.

%% 自动升级身上的宝石
auto_upgrade([] = WearGems, _BagGems, CostGems) ->
    {WearGems, CostGems};
auto_upgrade(WearGems, BagGems, CostGems) ->
    [Wear|T] = lists:keysort(#gems_info_temp.lv, WearGems),
    #gems_info_temp{type = GemType, lv = GemLv} = Wear,
    NextLv = GemLv+1,
    NextLvGem = data_equipment:get_equipment_gem_by_lv(GemType, GemLv+1),
    if
        NextLvGem == 0 ->
            %% 等级最低的都满级了，那就不用继续了
            {WearGems, CostGems};
        true ->
            case circle_calc_upgrade_gems(BagGems, GemType, 2, GemLv, []) of
                false ->
                    %% 等级最低的都不能升级了，那就不用继续了
                    %% 因为，假如后面等级高的可以升级会跟前面第一步镶嵌矛盾
                    {WearGems, CostGems};
                {NewBagGems, Cost} ->
                    %% 成功升一级，继续循环检查能否升级
                    NewWear = Wear#gems_info_temp{lv = NextLv, goods_id = NextLvGem, update = true},
                    auto_upgrade([NewWear|T], NewBagGems, Cost ++ CostGems)
            end
    end.

%% 计算升一级所需要的宝石
circle_calc_upgrade_gems(_BagGems, _Type, _NeedNum, 0, _Cost) -> false;
circle_calc_upgrade_gems(BagGems, Type, NeedNum, Lv, Cost) ->
    GoodsId = data_equipment:get_equipment_gem_by_lv(Type, Lv),
    Have = case lists:keyfind(GoodsId, #gems_info_temp.goods_id, BagGems) of
        #gems_info_temp{num = Num}  = GoodsInfo ->
            Num;
        _ ->
            GoodsInfo =  #gems_info_temp{},
            0
    end,
    if
        Have =:= NeedNum  ->
            {
                lists:keydelete(GoodsId, #gems_info_temp.goods_id, BagGems),
                [{GoodsId, NeedNum}|Cost]
            };
        Have > NeedNum ->
            {
                lists:keyreplace(GoodsId, #gems_info_temp.goods_id, BagGems, GoodsInfo#gems_info_temp{num = Have - NeedNum}),
                [{GoodsId, NeedNum}|Cost]
            };
        true ->
            NewNeedNum = (NeedNum - Have) * 3,
            %% 需要扣除宝石，则更新背包
            NewCost = case Have =:= 0 of
                          true  ->
                              NewBagGems = BagGems,
                              Cost;
                          false ->
                              NewBagGems = lists:keydelete(GoodsId, #gems_info_temp.goods_id, BagGems),
                              [{GoodsId, Have}|Cost]
                      end,
            circle_calc_upgrade_gems(NewBagGems, Type, NewNeedNum, Lv-1, NewCost)
    end.

%% 背包里是否有大于Lv的宝石
check_gem_to_inlay(Lv, [BagGem|T]) ->
    #gems_info_temp{num = Num, lv = GemLv, goods_id = GemId} = BagGem,
    case GemLv > Lv
        andalso Num > 0
    of
        true  ->
            %% 可以镶嵌，那么需要将背包里的宝石数量减一
            case Num > 1 of
                true  ->
                    {GemId, GemLv, [BagGem#gems_info_temp{num = Num - 1}|T]};
                false ->
                    {GemId, GemLv, T}
            end;
        false ->
            false
    end.

%% 把部位的每个槽位都构造一条数据
get_pos_type_gem(Pos, GemType, Gems) ->
    case lists:keyfind(GemType, 1, Gems) of
        {_, GemId} ->
            case data_equipment:get_equipment_gem_info(GemId) of
                #base_equipment_gems{lv = Lv} ->
                    #gems_info_temp{pos = Pos, type = GemType, lv = Lv, goods_id = GemId, old_gem = GemId};
                _ ->
                    #gems_info_temp{pos = Pos, type = GemType}
            end;
        _ ->
            #gems_info_temp{pos = Pos, type = GemType}
    end.
%%---------------------------------------------------------------------
%% @doc 守护跟随
%%---------------------------------------------------------------------
%% 守护跟随状态
equipment_follow_guard(Player, GoodsId, Status) ->
    #player_status{equipment = StatusEquipment} = Player,
    #equipment{follow_guard = FollowGuard} = StatusEquipment,
    case common_check(GoodsId, ?GOODS_LOC_EQUIP) of
        {ok, #goods{goods_id = GoodsTypeId}, _EquipmentConfig} ->
            if
                Status =:= 1 andalso GoodsTypeId =:= FollowGuard -> {false, 2};
                Status =:= 0 andalso GoodsTypeId =/= FollowGuard -> {false, 3};
                true ->
                    NewFollow =
                    case Status of
                        1 -> GoodsTypeId;
                        0 -> 0
                    end,
                    Player1 = update_follow_guard(Player, GoodsId, NewFollow, Status),
                    {true, Player1}
            end;
        _ ->
            {false, 4}
    end.

update_follow_guard(Player, GoodsId, NewFollow, Status) ->
    #player_status{
        id = RoleId,
        equipment = RoleEquipment,
        figure = Figure
    } = Player,
    NewFigure = Figure#figure{follow_guard = NewFollow},
    NewRoleEquipment = RoleEquipment#equipment{follow_guard = NewFollow},
    save_equipment_info(RoleId, NewRoleEquipment),
    %% 公共缓存暂时用不到，不做更新，因为也没有初始化
    %lib_role:update_role_show(RoleId, [{follow_guard, GoodsId}]),
    mod_scene_agent:update(Player, [{follow_guard, NewFollow}]),
    lib_scene:broadcast_player_scene_info(Player, [{?SCENE_USER_UPDATE_FOLLOW_GUARD, NewFollow}]),
    lib_server_send:send_to_sid(Player#player_status.sid, pt_152, 15221, [GoodsId, Status]),
    Player#player_status{equipment = NewRoleEquipment, figure = NewFigure}.

%%---------------------------------------------------------------------
%% @doc 可成长属性
%%---------------------------------------------------------------------
%% 专属幻魔
kill_personal_boss(Player) -> % lib_dungeon_personal_boss:player_kill_personal_boss
    kill_personal_boss(Player, 1).
kill_personal_boss(Player, Count) -> % lib_dungeon_personal_boss:player_kill_personal_boss
    kill_boss(Player, ?EQUIPMENT_KILL_PERSONAL_BOSS, kill, Count).
%% 赏金幻魔
kill_reward_boss(Player) -> % local_boss_api:265
    kill_boss(Player, ?EQUIPMENT_KILL_REWARD_BOSS, kill).
%% 星域幻魔
kill_deserted_boss(Player) -> % deserted_boss_mgr:role_boss_die_handle
    kill_boss(Player, ?EQUIPMENT_KILL_DESERTED_BOSS, kill).
%% 幻魔之家击杀
kill_home_boss(Player) ->
    %% 有上限,击杀次数在这之前已经+1了
    DailyKilled = lib_daily:get_count(Player#player_status.id, ?MOD_HOME_BOSS, ?COUNT_HOME_BOSS_KILL),
    Max = lib_daily:get_count_limit(?MOD_EQUIP, ?EQUIPMENT_HOME_BOSS_KILL, 0),
    case DailyKilled > Max of
        true  ->
            Player;
        false ->
            kill_boss(Player, ?EQUIPMENT_KILL_HOME_BOSS, kill)
    end.

%% 赏金幻魔协助击杀
help_kill_reward_boss(Player) ->
    help_kill_boss(Player, ?EQUIPMENT_KILL_REWARD_BOSS).
%% 星域幻魔协助击杀
help_kill_deserted_boss(Player) ->
    help_kill_boss(Player, ?EQUIPMENT_KILL_DESERTED_BOSS).

help_kill_boss(Player, BossType) ->
    DailyCount = lib_daily:get_count(Player#player_status.id, ?MOD_EQUIP, ?EQUIPMENT_HELP_SP_ATTR, BossType),
    Max = lib_daily:get_count_limit(?MOD_EQUIP, ?EQUIPMENT_HELP_SP_ATTR, BossType),
    case DailyCount >= Max of
        true  ->
            Player;
        false ->
            kill_boss(Player, BossType, help)
    end.

kill_boss(Player, BossType, Kind) ->
    kill_boss(Player, BossType, Kind, 1).
kill_boss(Player, BossType, Kind, Count) ->
    case data_equipment:get_equipment_by_mon(BossType) of
        [Pos] ->
            #player_status{equipment = Equipment} = Player,
            #equipment{equipment_series = SeriesInfo} = Equipment,
            UnlockSeries = transform:get_unlock_equipment_series(Player),
            EquipmentList   = equipment_api:get_all_equipment_list(),
            {NewSeriesInfo, Update, RefreshPower} = lists:foldl(
                fun(Series, {TempAll, TempUpdate, Refresh}) ->
                    case equipment_api:is_series_pos_equip(EquipmentList, Series, Pos) of
                        true  ->
                            %% 需要穿戴装备才能成长属性
                            #equipment_series{sp_lv = SpLv, sp_exp = SpExp} =
                            SeriesPos = equipment_api:get_equipment_series_pos_info(TempAll, Series, Pos),
                            %% 自己击杀和协助击杀经验值不一样
                            AddExp = case data_equipment:get_kill_mon_exp(Series, Pos) of
                                [{Exp, _}] when Kind == kill ->
                                    Exp;
                                [{_, Exp}] when Kind == help ->
                                    Exp;
                                _ ->
                                    0
                            end,
                            NewExp = SpExp + AddExp * Count,
                            {NextLv, NextExp} = sp_upgrade(Series, Pos, SpLv, NewExp),
                            case {NextLv, NextExp} of
                                {SpLv, SpExp} ->
                                    %% 没有变化，一般是满级了
                                    {TempAll, TempUpdate, Refresh};
                                _ ->
                                    NewSeriesPos = SeriesPos#equipment_series{sp_lv = NextLv, sp_exp = NextExp},
                                    {
                                        equipment_api:set_equipment_series_pos_info(TempAll, NewSeriesPos),
                                        [NewSeriesPos|TempUpdate],
                                        true
                                    }
                            end;
                        false ->
                            {TempAll, TempUpdate, Refresh}
                    end
                end, {SeriesInfo, [], false}, lists:seq(1, UnlockSeries)
            ),
            batch_equipment_series_pos_info(Player#player_status.id, Update),
            %% 协助次数
            Kind == help andalso lib_daily:increment(Player#player_status.id, ?MOD_EQUIP, ?EQUIPMENT_HELP_SP_ATTR, BossType),
            Player1 = Player#player_status{equipment = Equipment#equipment{equipment_series = NewSeriesInfo}},
            case RefreshPower of
                true  ->
                    equipment_api:update_equipment_power(Player1);
                false ->
                    Player1
            end;
        _ ->
            Player
    end.

%% 连续升级
sp_upgrade(Series, Pos, SpLv, Exp) ->
    case data_equipment:get_equipment_sp_attr(Series, Pos, SpLv) of
        [{_Attr, NeedExp}] when NeedExp > 0 andalso Exp >= NeedExp ->
            sp_upgrade(Series, Pos, SpLv + 1, Exp - NeedExp);
        [{_Attr, 0}] ->
            {SpLv, 0};
        _ ->
            {SpLv, Exp}
    end.
    
%%---------------------------------------------------------------------
%% @doc Internal Function
%%---------------------------------------------------------------------
common_check(GoodsId, Location) ->
    case lib_goods_api:get_goods_info(GoodsId, Location) of
        #goods{goods_id = GoodsTypeId} = GoodsInfo ->
            case data_equipment:get_equipment_info(GoodsTypeId) of
                #base_equipment{} = EquipmentConfig ->
                    {ok, GoodsInfo, EquipmentConfig};
                _ ->
                    {false, ?ERRCODE(err152_missing_cfg)}
            end;
        _ ->
            {false, ?ERRCODE(err152_not_exist)}
    end.

%%---------------------------------------------------------------------
%% @doc 保存装备数据
%%---------------------------------------------------------------------
save_equipment_info(RoleId, Equipment) ->
    #equipment{stars = Stars, stars_info = StarsInfo, unlock_series = UnlockSeries, follow_guard = FollowGuard, gems_suit = GemsSuit} = Equipment,
    db:execute(?SQL_UPDATE_EQUIPMENT, [RoleId, Stars, util:term_to_bitstring(StarsInfo), UnlockSeries, FollowGuard, util:term_to_bitstring(GemsSuit)]),
    ok.

%% 保存位置数据
save_equipment_series_pos_info(RoleId, EquipmentSeries) ->
    #equipment_series{series = Series, pos = Pos, gems = Gems, empower = Empower, sp_lv = SpLv, sp_exp = SpExp} = EquipmentSeries,
    db:execute(?SQL_UPDATE_EQUIPMENT_SERIES, [RoleId, Series, Pos, util:term_to_bitstring(Gems), Empower, SpLv, SpExp]).

%% 批量保存数据
batch_equipment_series_pos_info(_RoleId, []) -> ok;
batch_equipment_series_pos_info(RoleId, AllGems) ->
    SqlList =
        [
            io_lib:format(<<"(~p,~p,~p,'~ts',~p,~p,~p)">>, [RoleId, Series, Pos, util:term_to_bitstring(Gems), Empower, SpLv, SpExp])
            || #equipment_series{series = Series, pos = Pos, gems = Gems, empower = Empower, sp_lv = SpLv, sp_exp = SpExp} <- AllGems
        ],
    SQL = io_lib:format(?SQL_UPDATE_EQUIPMENT_SERIES_BATCH, [string:join(SqlList, ",")]),
    db:execute(SQL).
%%---------------------------------------------------------------------
%% @doc 日志
%%---------------------------------------------------------------------
log_equipment_strength(Player, StrengthConfig, GoodsInfo, Result, LvBefore, LvAfter, Ratio) ->
    #player_status{id = RoleId, figure = #figure{name = Name}} = Player,
    lib_log_api:log_equipment_strength(RoleId, Name, StrengthConfig#base_equipment_strength.series,
        StrengthConfig#base_equipment_strength.equip_type, GoodsInfo#goods.id, GoodsInfo#goods.goods_id,
        Result, LvBefore, LvAfter, Ratio, StrengthConfig#base_equipment_strength.cost).

log_equipment_great(Player, EquipmentGreatConfig, OldLv, NextLv, NewGoodsInfo, Result, Ratio, ExtraCostGoods, Cost) ->
    FinalCost = [{0, CostGoodsId, GoodsNum}||{#goods{goods_id = CostGoodsId}, GoodsNum} <- ExtraCostGoods] ++ Cost,
    #player_status{id = RoleId, figure = #figure{name = Name}} = Player,
    lib_log_api:log_equipment_great(RoleId, Name, EquipmentGreatConfig#base_equipment_great.series, EquipmentGreatConfig#base_equipment_great.equip_type,
        NewGoodsInfo#goods.id, NewGoodsInfo#goods.goods_id, Result, OldLv, NextLv, Ratio, FinalCost).

log_equipment_inherit(Player, Type, OldGoodsInfo, NewGoodsInfo, CostGoods) ->
    #player_status{id = RoleId, figure = #figure{name = Name}} = Player,
    lib_log_api:log_equipment_inherit(RoleId, Name, Type, NewGoodsInfo#goods.id, NewGoodsInfo#goods.goods_id,
        CostGoods, NewGoodsInfo#goods.extra_data, OldGoodsInfo#goods.extra_data).

