%%%-------------------------------------------------------------------
%%% @author liushl
%%% @doc
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(drop_logic).
-author("liushl").

-include("common.hrl").
-include("def_fun.hrl").
-include("custom_act.hrl").
-include("drop.hrl").
-include("scene.hrl").
-include("def_goods.hrl").
-include("server.hrl").
-include("figure.hrl").
-include("dungeon.hrl").
-include("def_module.hrl").
-include("goods.hrl").
-include("team.hrl").
-include("local_boss.hrl").
%% API
-export([]).
-compile(export_all).

%% 统计掉落key
-define(DropKey(Type), {?MOD_GOODS, ?MOD_GOODS_DROP, Type}).
%% 次数
-define(LIMIT_DAY_1, 1).
-define(LIMIT_DAY_7, 7).

%% BOSS之家掉落控制子类包括 橙装碎片 44，紫装 3 外观碎片，4

%% 赏金幻魔/星域幻魔/专属幻魔/小队讨伐
%% 这类击杀，如果橙装碎片没有掉落，则每次击杀后对橙装碎片掉落概率进行加成
%% 玩家BUFF计算掉落
calc_drop_goods(Player, MonArgs, DropRule, Career, DropRoles) ->
    calc_drop_goods(Player, MonArgs, DropRule, Career, 0, DropRoles).
calc_drop_goods(Player, MonArgs, DropRule, Career, TeamBuff, DropRoles) ->
    #player_status{scene=Scene, team = TeamInfo}=Player,
    #ets_drop_rule{drop_rule=Rules} = DropRule,
    %% 橙装掉落权重加成
    PublicBuffRatio   = calc_public_buff(Player),
    PersonalBuffRatio = calc_personal_buff(Player, MonArgs, TeamBuff),
    OrangeWeighAdd = PublicBuffRatio + PersonalBuffRatio,
    %% 橙装碎片掉落宏观调控
    ChipWeightAdd = drop_equipment_chip_control_weight(Player, MonArgs),
    %% BOSS之家掉落宏观调控
    {PurpleWeightAdd, FosterChipAdd} = purple_equipment_control_weight(Player, MonArgs, DropRoles, TeamInfo),
    ExpSea = exp_sea_logic:is_exp_sea_scene(Scene),
    NewRules =
        case OrangeWeighAdd > 0 orelse ChipWeightAdd > 0 orelse PurpleWeightAdd > 0 orelse ExpSea orelse FosterChipAdd > 0 of
            true  ->
                [
                    begin
                        NewDropList =
                            [
                                %% 因为掉落规则的配置方式是每一个掉落列表里都是同一种类型物品，
                                %% 所以每次只会有一种结果
                                case has_orange_equipment(Drop#drop.list) of
                                    {true, orange_equipment_chip} when ChipWeightAdd > 0 ->
                                        %% 橙装碎片
                                        Drop#drop{weight = round(Drop#drop.weight + ChipWeightAdd)};
                                    {true, orange_equipment} when OrangeWeighAdd > 0 ->
                                        %% 橙装
                                        Drop#drop{weight = round(Drop#drop.weight + Drop#drop.weight * OrangeWeighAdd / 100)};
                                    {true, purple_equipment} when PurpleWeightAdd > 0 ->
                                        %% 紫装
                                        Drop#drop{weight = round(Drop#drop.weight + PurpleWeightAdd)};
                                    {true, foster_skin_chip} when FosterChipAdd > 0 ->
                                        %% 进阶珍宝碎片
                                        Drop#drop{weight = round(Drop#drop.weight + FosterChipAdd)};
                                    _ when ExpSea == true ->
                                        SeaAdd = calc_sea_buff(Drop#drop.list,Player),
                                        Drop#drop{weight = round(Drop#drop.weight + SeaAdd)};
                                    _->
                                        Drop
                                end
                                || Drop <- DropList
                            ],
                        {Round, NewDropList}
                    end
                    || {Round, DropList} <- Rules
                ];
            false ->
                Rules
        end,
    calc_drop_goods(NewRules, Career).

%% 公共活动提供的BUFF加成
calc_public_buff(Player) ->
    %% 定制活动buff
    UniteBuffRatio = custom_unite_buff:calc_drop_ratio(Player),
    UniteBuffRatio.

%% 个人差异提供的BUFF加成，一般是组队情形
%% 外来参数为0，则计算自己加成
calc_personal_buff(Player, MonArgs, 0) ->
    %% 玩家buff
    %GoodsDropBuff = lib_player:get_goods_drop_add_ratio(Player),
    %% 特权卡buff
    WelfareRatio = lib_recharge_welfare:get_welfare_radio(Player, MonArgs),
    WelfareRatio;
%% 外来参数不为0，则直接使用外来参数，一般是队伍里最高加成
calc_personal_buff(_Player, _MonArgs, TeamBuff) -> TeamBuff.

%%计算经验海时段加成
calc_sea_buff([],_player)->
    0;
calc_sea_buff([{RuleId,_}|T],Player)->
    case data_drop:get_goods_id(RuleId) of
        [Id|_] ->
            case data_drop:get_goods(Id) of
                #ets_drop_goods{goods_id = GId} ->
                    TimeZ = exp_sea_logic:get_in_scene_time(Player),
                    data_exp_sea:get_sea_buff(GId,TimeZ);
                _ ->
                    calc_sea_buff(T,Player)
            end;
        _ ->
            calc_sea_buff(T,Player)
    end.

%% 掉落列表是否有橙装-只判断第一个物品
has_orange_equipment([{RuleId, _}|T]) ->
    case data_drop:get_goods_id(RuleId) of
        [Id|_] ->
            case data_drop:get_goods(Id) of
                #ets_drop_goods{goods_id = GoodsId} ->
                    case is_color_goods(GoodsId) of
                        false ->
                            has_orange_equipment(T);
                        {true, _} = Res ->
                            Res
                    end;
                _ ->
                    has_orange_equipment(T)
            end;
        _ ->
            has_orange_equipment(T)
    end;
has_orange_equipment(_) ->
    false.

%% 物品列表是否有橙装碎片
has_orange_equipment_chip_list([]) -> false;
has_orange_equipment_chip_list([{_, GoodsId, _}|T]) ->
    case is_color_goods(GoodsId) of
        {true, orange_equipment_chip} ->
            true;
        _ ->
            has_orange_equipment_chip_list(T)
    end.

%% 物品列表是否有紫装
has_purple_equipment_list([]) -> false;
has_purple_equipment_list([{_, GoodsId, _}|T]) ->
    case data_goods_type:get(GoodsId) of
        {true, purple_equipment} ->
            true;
        _ ->
            has_purple_equipment_list(T)
    end.


is_color_goods(GoodsId) ->
    case data_goods_type:get(GoodsId) of
        #ets_goods_type{type = ?GOODS_TYPE_EQUIP, color = ?ORANGE} ->
            {true, orange_equipment};
        #ets_goods_type{type = ?GOODS_TYPE_EQUIP, color = ?PURPLE} ->
            {true, purple_equipment};
        #ets_goods_type{type = ?GOODS_TYPE_STUFF, subtype = ?GOODS_STUFF_SUBTYPE_EQUIPMENT_CHIP} ->
            {true, orange_equipment_chip};
        #ets_goods_type{type = ?GOODS_TYPE_X6, subtype = SubType}
            when SubType == 6 orelse SubType == 7 orelse SubType == 8 orelse SubType == 9 ->
            %% 外观碎片
            {true, foster_skin_chip};
        _ -> false
    end.
%%%-------------------------------------------------------------------
%%% @doc
%%%     宏观调控橙装权重加成
%%% @end
%%%-------------------------------------------------------------------
%% 获得宏观调控权重加成
drop_equipment_chip_control_weight(Player, MonArgs) ->
    IsNeed = is_need_control(MonArgs),
    case IsNeed of
        true  ->
            MaxDay = data_drop_limit:get_max_day(),
            Opday = util:get_open_day(),
            case Opday =< MaxDay of
                true  ->
                    DropOrigin = to_drop_cfg_origin(MonArgs),
                    NowCount = lib_daily:get_count(Player#player_status.id, ?MOD_GOODS,
                        ?MOD_GOODS_DROP_COUNT_NONE, DropOrigin),
                    %% 第N次击杀权重加成
                    data_drop_limit:get_boss_drop_control_add(DropOrigin, Opday, NowCount + 1);
                false ->
                    0
            end;
        false ->
            0
    end.

%% 如果掉落橙装碎片，那就重制调控数值
check_adjust_equipment_chip_control(Player, DropList, MonArgs, KList) ->
    IsNeed = is_need_control(MonArgs),
    case IsNeed of
        true  ->
            MaxDay = data_drop_limit:get_max_day(),
            Opday = util:get_open_day(),
            case Opday =< MaxDay of
                true  ->
                    DropOrigin = to_drop_cfg_origin(MonArgs),
                    case has_orange_equipment_chip_list(DropList) of
                        true  ->
                            %% 如果掉落了，直接清0数值
                            %% 如果是星域幻魔组队击杀，则清空所有队员加成数值
                            case is_deserted_boss(MonArgs) of
                                true  ->
                                    [
                                        lib_daily:set_count_offline(One#mon_atter.id, ?MOD_GOODS, ?MOD_GOODS_DROP_COUNT_NONE,
                                            DropOrigin, 0)
                                        || One <- lists:keydelete(Player#player_status.id, #mon_atter.id, KList)
                                    ];
                                false ->
                                    ignore
                            end,
                            lib_daily:set_count(Player#player_status.id, ?MOD_GOODS, ?MOD_GOODS_DROP_COUNT_NONE,
                                DropOrigin, 0);
                        false ->
                            %% 相应的未掉落次数直接+1
                            lib_daily:increment(Player#player_status.id, ?MOD_GOODS, ?MOD_GOODS_DROP_COUNT_NONE,
                                DropOrigin)
                    end;
                false ->
                    ignore
            end;
        false ->
            ignore
    end.

%%%-------------------------------------------------------------------
%%% @doc
%%%     幻魔之家紫装掉落控制
%%% @end
%%%-------------------------------------------------------------------
purple_equipment_control_weight(Player, MonArgs, DropRoles, TeamInfo) ->
    case MonArgs#mon_args.boss  =:= ?MON_HOME_BOSS orelse
        MonArgs#mon_args.boss  =:= ?MON_HOME_GBOSS of
        true  ->
            %% 单人情形，击杀次数+1在掉落判断之后；组队情形，击杀次数+1在前，因为要掉落通过队伍进程中转，所以掉落慢一点
            CheckCount =
                case TeamInfo#status_team.team_id > 0 of
                    true  ->
                        %% 组队情形以最少的那个玩家为准
                        lists:min(
                            [
                                lib_daily:get_count_offline(Id, ?MOD_HOME_BOSS, ?COUNT_HOME_BOSS_KILL)
                                || #mon_atter{id = Id} <- DropRoles
                            ]
                        );
                    false ->
                        DailyKilled = lib_daily:get_count(Player#player_status.id, ?MOD_HOME_BOSS, ?COUNT_HOME_BOSS_KILL),
                        DailyKilled + 1
                end,
            %% 每次都1/2必掉，之后通过上限值来控制
            case data_drop_limit:get_hme_boss_weight(CheckCount) of
                Add when is_integer(Add)->
                    {Add, 5000};
                _ ->
                    {0, 5000}
            end;
        false ->
            {0, 0}
    end.

%%%-------------------------------------------------------------------
%%% @doc
%%%     通用掉落物品计算，根据掉落规则计算掉落物品
%%% @end
%%%-------------------------------------------------------------------
calc_drop_goods(DropRule, Career)->
    %% 计算掉落次数 [{DropId, Num}]
    DropNumList = get_drop_num_list(DropRule),
    {StableDropGoods, RandDropGoods} = drop_goods_list(DropNumList, Career),
    {StableDropGoods, RandDropGoods}.

%% 取随机物品掉落数列表
%% 后台展示的掉落规则：1|1:1=10000
%% A|B:C=D
%% A 随机A次
%% B:C=D 权重D使得掉落id B 掉落 C 次
%% @return [{DropId, Num}]
get_drop_num_list(DropRule) when is_record(DropRule, ets_drop_rule)->
    get_drop_num_list_helper(DropRule#ets_drop_rule.drop_rule, []);
get_drop_num_list(DropList) ->
    get_drop_num_list_helper(DropList, []).
get_drop_num_list_helper([], ResList) -> ResList;
get_drop_num_list_helper([{Round, WeightList}|T], ResList) ->
    WeightList2 = [{List, Weight}||#drop{list = List, weight = Weight}<-WeightList],
    F = fun(_I, AccList) ->
        find_ratio(WeightList2, 0, urand:rand(1, ?RATIO_MAX)) ++ AccList
        end,
    DropList = lists:foldl(F, [], lists:seq(1, Round)),
    get_drop_num_list_helper(T, DropList ++ ResList).
%% 查找匹配机率的值
find_ratio([], _, _) -> [];
find_ratio([{L,R}|_], S, Ra) when S < Ra andalso Ra =< (S + R) -> L;
find_ratio([{_,R}|T], S, Ra) -> find_ratio(T, (S + R), Ra).

%% 计算掉落列表
drop_goods_list([], _Career) ->
    {[], []};
drop_goods_list(DropNumList, Career) ->
    F = fun({DropId, DropNum}, {AccStableDropList, AccRandDropList}) ->
        {StableDropList, RandDropList} = drop_goods(DropId, DropNum, Career),
        {StableDropList ++ AccStableDropList, RandDropList ++ AccRandDropList}
        end,
    lists:foldl(F, {[], []}, DropNumList).

%% 根据单个掉落id：N， 获取相同掉落id：N的掉落物品
drop_goods(_DropId, DropNum, _Career) when DropNum =< 0 ->
    {[], []};
drop_goods(DropId, DropNum, Career) ->
    DropList = data_drop:get_goods_id(DropId),
    [StableGoods, _TaskGoods, RandGoods] = get_drop_goods_list(DropList, Career),
    %% 固定掉落
    F = fun(_I, AccList) -> StableGoods ++ AccList end,
    StableDropList = lists:foldl(F, [], lists:seq(1, DropNum)),
    
    %% 随机掉落
    RandDropList = case length(RandGoods) =:= 0 of
                       true -> [];
                       false -> %% 获取所有相同掉落id的总的概率
                           TotalRatio = get_goods_ratio(RandGoods, 0),
                           find_drop_goods(RandGoods, DropNum, TotalRatio, [])
                   end,
    {StableDropList, RandDropList}.

%% 取掉落物品列表
get_drop_goods_list(DropList, Career) when is_list(DropList)->
    {_,{Hour,_,_}} = calendar:local_time(),
    NowTime = utime:unixtime(),
    get_drop_goods_list(DropList, Career, NowTime, Hour, [], [], []).
get_drop_goods_list([], _Career, _NowTime, __Hour, StableGoods, TaskGoods, RandGoods) ->
    [StableGoods, TaskGoods, RandGoods];
get_drop_goods_list([Id|DropList], Career, NowTime, Hour, StableGoods, TaskGoods, RandGoods) ->
    [NStableGoods, NTaskGoods, NRandGoods] =
        case data_drop:get_goods(Id) of
            #ets_drop_goods{time_start = TimeS, time_end = TimeEnd,
                hour_start = HStart, hour_end = HEnd, type = DropType} = Drop ->
                if
                    (TimeS > 0 andalso TimeS > NowTime) orelse
                        (TimeEnd > 0 andalso TimeEnd < NowTime) orelse
                        (HStart > 0 andalso HStart > Hour) orelse
                        (HEnd > 0 andalso HEnd < Hour) ->
                        [StableGoods, TaskGoods, RandGoods];
                    DropType =:= ?DROP_TYPE_FIXED ->
                        [[Drop|StableGoods], TaskGoods, RandGoods];
                    DropType =:= ?DROP_TYPE_TASK ->
                        NewDrop = modify_career_goods_ratio(Drop, Career),
                        [StableGoods, [NewDrop|TaskGoods], RandGoods];
                    DropType =:= ?DROP_TYPE_RAND ->
                        NewDrop = modify_career_goods_ratio(Drop, Career),
                        [StableGoods, TaskGoods, [NewDrop|RandGoods]];
                    true ->
                        [StableGoods, TaskGoods, RandGoods]
                end;
            _ ->
                [StableGoods, TaskGoods, RandGoods]
        end,
    get_drop_goods_list(DropList, Career, NowTime, Hour, NStableGoods, NTaskGoods, NRandGoods).
modify_career_goods_ratio(Drop, 0) -> Drop;
modify_career_goods_ratio(Drop, Career) ->
    #ets_drop_goods{career_ratio_list = CareerRatioList} = Drop,
    case lists:keyfind(Career, 1, CareerRatioList) of
        {Career, CareerRatio} -> Drop#ets_drop_goods{ratio = CareerRatio};
        _ -> Drop
    end.

%% 统一个掉落id，随机多次掉落
find_drop_goods(DropList, DropNum, TotalRatio, Result) ->
    %% 随机掉落id里面的物品，找到一个掉落
    case find_goods(DropList, TotalRatio) of
        [] -> Result;
        DropGoods ->
            NewDropGoods = rand_item(DropGoods),
            NewResult = case NewDropGoods of
                            #ets_drop_goods{goods_id = GoodsId} when GoodsId =< 0 ->
                                Result;
                            _ ->
                                [NewDropGoods | Result]
                        end,
            if
                DropNum > 1 ->
                    case DropGoods of
                        #ets_drop_goods{repeat = 1} ->
                            find_drop_goods(DropList, (DropNum - 1), TotalRatio, NewResult);
                        _ ->
                            %% 不放回
                            find_drop_goods(lists:delete(DropGoods, DropList), (DropNum - 1), TotalRatio, NewResult)
                    end;
                true ->
                    NewResult
            end
    end.
%% 随机物品数
rand_item(DropGoods) ->
    Num = case DropGoods#ets_drop_goods.num > 1 andalso DropGoods#ets_drop_goods.min_num >= 1 of
              true -> urand:rand(DropGoods#ets_drop_goods.min_num, DropGoods#ets_drop_goods.num);
              false -> 1
          end,
    DropGoods#ets_drop_goods{num=Num}.

%% 查找匹配机率的值
find_goods([H], _TotalRatio) ->
    %% 只有一个道具，那就掉它了
    H;
find_goods(DropList, TotalRatio) ->
    Rand = urand:rand(1, TotalRatio),
    find_goods(DropList, 0, Rand).

find_goods([], _, _) -> [];
find_goods([H|_], S, Ra) when Ra > S andalso Ra =< (S + H#ets_drop_goods.ratio) -> H;
find_goods([H|T], S, Ra) -> find_goods(T, (S + H#ets_drop_goods.ratio), Ra).

%% 查找匹配机率的值
get_goods_ratio([], R) -> R;
get_goods_ratio([H|T], R) ->
    get_goods_ratio(T, H#ets_drop_goods.ratio + R).

%%%-------------------------------------------------------------------
%%% @doc
%%%     定制活动额外掉落
%%% @end
%%%-------------------------------------------------------------------
calc_act_drop_goods(MonArgs, Career)->
    %% 掉落活动可能多个主类
    ActIds = data_drop_mon:get_act_type_list(),
    case lib_custom_act_api:get_custom_act_open_info_by_actids(ActIds) of
        [] -> [];
        OpenActInfos -> %% 开启所有掉落活动
            NowTime = utime:unixtime(),
            #mon_args{boss = MonType, lv = MonLv} = MonArgs,
            calc_all_act_mon_drop(OpenActInfos, Career, NowTime, MonType, MonLv, [])
    end.

calc_all_act_mon_drop([], _Career, _NowTime, _MonType, _MonLv, DropGoods) ->
    DropGoods;
calc_all_act_mon_drop([#act_info{key = {ActId, SubId}, stime = STime}|OpenActInfos],
    Career, NowTime, MonType, MonLv, DropGoods) ->
    NewDropGoods =
        case lib_custom_act_util:get_act_cfg_info(ActId, SubId) of
            [] -> DropGoods;
            #custom_act_cfg{condition = Condition} ->
                case lists:keyfind(sp_gap_time, 1, Condition) of
                    false -> CandDrop = true;
                    {_, Day} -> CandDrop = ?IF(NowTime > STime + Day*?ONE_DAY_SECONDS, false, true)
                end,
                if
                    CandDrop ->
                        case data_drop_mon:get_mon_drop_rule(ActId, SubId, MonType, MonLv) of
                            #base_mon_type_drop{drop_rule = DropRule} ->
                                {StableDropGoods, RandDropGoods} = calc_drop_goods(DropRule, Career),
                                StableDropGoods ++ RandDropGoods;
                            _ ->
                                []
                        end;
                    true -> DropGoods
                end
        end,
    calc_all_act_mon_drop(OpenActInfos, Career, NowTime, MonType, MonLv, NewDropGoods);
calc_all_act_mon_drop([_|OpenActInfos], Career, NowTime, MonType, MonLv, DropGoods) ->
    calc_all_act_mon_drop(OpenActInfos, Career, NowTime, MonType, MonLv, DropGoods).

%%%-------------------------------------------------------------------
%%% @doc
%%%     计算限时掉落: 一般是必掉物品
%%% @end
%%%-------------------------------------------------------------------
%%计算限时掉落
calc_act_limit_drop_goods(#mon_args{boss = ?MON_ACT_BOSS}, _PlayerLv)->
    case lib_custom_act_util:get_open_subtype_list(?CUSTOM_ACT_TYPE_BOSS) of
        [#act_info{key = {Type, SubType}}|_] ->
            case lib_custom_act_util:get_act_cfg_info(Type, SubType) of
                #custom_act_cfg{condition = Condition} ->
                    case lists:keyfind(drop_goods, 1, Condition) of
                        {_, GoodsId, GoodsNum} ->
                            [
                                trans_to_ets_drop(GoodsId, 1) || _ <- lists:seq(1, GoodsNum)
                            ];
                        _ ->
                            []
                    end;
                _ ->
                    []
            end;
        _ ->
            []
    end;
calc_act_limit_drop_goods(MonArgs,PlayerLv)->
    #mon_args{boss = MonType,scene=Scene} = MonArgs,
    DropTypes = data_custom_act:get_way_drops(MonType),
    case length(DropTypes)>0 of
        true when MonType =:= ?MON_DUN_BOSS ->
            case lists:member(Scene,[2081,2082,2083,2084,2085,2086]) of
                true->
                    Opens = lib_custom_act_util:get_open_subtype_list(?CUSTOM_ACT_THEME_LIMIT_DROP),
                    Drops = calc_all_limit_drop(Opens,MonType,PlayerLv,[]),
                    [trans_to_ets_drop(GoodsId,Num)||{_,GoodsId,Num}<-Drops];
                _->
                    []
            end;
        true when MonType>0->
            Opens = lib_custom_act_util:get_open_subtype_list(?CUSTOM_ACT_THEME_LIMIT_DROP),
            Drops = calc_all_limit_drop(Opens,MonType,PlayerLv,[]),
            [trans_to_ets_drop(GoodsId,Num)||{_,GoodsId,Num}<-Drops];
        _->
            []
    end.

calc_all_limit_drop([],_MonType,_PlayerLv,Drops)->
    Drops;
calc_all_limit_drop([H|T],MonType,PlayerLv,Drops)->
    #act_info{key={_,SubType}}=H,
    case lib_custom_act_util:get_act_cfg_info(?CUSTOM_ACT_THEME_LIMIT_DROP, SubType) of
        #custom_act_cfg{condition=Conditions} ->
            case lists:keyfind(drop_type,1,Conditions) of
                {_, DropType}->
                    skip;
                _->
                    DropType = none
            end,
            case lists:keyfind(role_lv,1,Conditions) of
                {_, OpenLv}->
                    skip;
                _->
                    OpenLv = none
            end,
            case PlayerLv>=OpenLv of
                true->
                    case data_custom_act:get_limit_drop(DropType,MonType) of
                        #custom_act_limit_drop{award=AwardWs} when DropType>100 ->
                            case lists:keyfind(drop_goods,1,Conditions) of
                                {_,GoodsId}->
                                    Num = urand:rand_with_weight(AwardWs),
                                    GDrop = {?TYPE_GOODS,GoodsId,Num},
                                    calc_all_limit_drop(T,MonType,PlayerLv,[GDrop|Drops]);
                                _->
                                    calc_all_limit_drop(T,MonType,PlayerLv,Drops)
                            end;
                        #custom_act_limit_drop{award=AwardWs}->
                            GDrop = urand:rand_with_weight(AwardWs),
                            calc_all_limit_drop(T,MonType,PlayerLv,[GDrop|Drops]);
                        _->
                            calc_all_limit_drop(T,MonType,PlayerLv,Drops)
                    end;
                _->
                    calc_all_limit_drop(T,MonType,PlayerLv,Drops)
            end;
        _ ->
            calc_all_limit_drop(T,MonType,PlayerLv,Drops)
    end.

trans_to_ets_drop(GoodsId,Num)->
    GDrop = data_drop:get_goods(1),
    GDrop#ets_drop_goods{goods_id=GoodsId,num=Num, min_num=Num}.

%%%-------------------------------------------------------------------
%%% @doc
%%%     副本双倍活动,掉落直接加倍
%%% @end
%%%-------------------------------------------------------------------
calc_multiple_act_drops(#player_status{figure = #figure{lv = Lv}, scene = SceneId, dungeon = #status_dungeon{dun_id = DunId}},
    #mon_args{scene = SceneId}, DropList) when DunId > 0 andalso DropList =/= [] ->
    OpenLv = lib_custom_act_util:get_act_open_lv(?CUSTOM_ACT_TYPE_DUN_MUL_DROP),
    case data_dungeon:get_dun(DunId) of
        #dun_cfg{type = DunType} when Lv >= OpenLv->
            case lib_dungeon_api:get_custom_act_drop_times(DunType) of
                N when N > 1 ->
                    ulists:elem_multiply(DropList, N);
                _ ->
                    DropList
            end;
        _ ->
            DropList
    end;
calc_multiple_act_drops(_, _, DropList) -> DropList.

%%%-------------------------------------------------------------------
%%% @doc
%%%     掉落上限
%%% @end
%%%-------------------------------------------------------------------
%% 获取掉落限制:分成全服掉落,个人掉落限制列表
get_drop_goods_limit(Ps, DropList) ->
    #player_status{scene=Scene,figure=#figure{lv=Lv}}=Ps,
    Day = util:get_open_day(),
    F = fun(Drop, {CoinNum, MyGoods, SerGoods}) ->
        #ets_drop_goods{drop_thing_type = DTType, goods_id = GoodsId, num = DNum} = Drop,
        if
            DTType == ?TYPE_GOODS ->
                case data_drop_limit:get_goods_limit(DTType, GoodsId) of
                    #base_drop_limit{limit_type = LimitType, limit_day = LimitDay}
                        when LimitType == ?DROP_LIMIT_GOODID ->
                        Key = {GoodsId, LimitDay},
                        case lists:keyfind(Key, 1, MyGoods) of
                            {Key, ODNum} ->
                                NewMyGoods = lists:keystore(Key, 1, MyGoods, {Key, DNum+ODNum}),
                                {CoinNum,NewMyGoods, SerGoods};
                            _ ->
                                {CoinNum, [{Key, DNum}|MyGoods], SerGoods}
                        end;
                    #base_drop_limit{limit_type = LimitType, limit_day = LimitDay}
                        when LimitType == ?DROP_LIMIT_ID_SERVER ->
                        Key = {GoodsId, LimitDay},
                        case lists:keyfind(Key, 1, SerGoods) of
                            {Key, ODNum} ->
                                NewSerGoods = lists:keystore(Key, 1, SerGoods, {Key, DNum+ODNum}),
                                {CoinNum, MyGoods, NewSerGoods};
                            _ ->
                                {CoinNum, MyGoods, [{Key, DNum}|SerGoods]}
                        end;
                    _ ->
                        ExpSea = exp_sea_logic:is_exp_sea_scene(Scene),
                        case data_exp_sea:get_sea_limit(GoodsId,Lv,Day)>0 of
                            true when ExpSea == true->
                                Key = {GoodsId, ?LIMIT_DAY_1},
                                case lists:keyfind(Key, 1, MyGoods) of
                                    {Key, ODNum} ->
                                        NewMyGoods = lists:keystore(Key, 1, MyGoods, {Key, DNum+ODNum}),
                                        {CoinNum,NewMyGoods,SerGoods};
                                    _ ->
                                        {CoinNum, [{Key, DNum}|MyGoods], SerGoods}
                                end;
                            _->
                                {CoinNum, MyGoods, SerGoods}
                        end
                end;
            DTType == ?TYPE_COIN ->
                {CoinNum+DNum, MyGoods, SerGoods};
            true ->
                {CoinNum, MyGoods, SerGoods}
        end
        end,
    %% 统计所有掉落的金币数量和物品数量
    {TCoinNum, TMyGoods, TSerGoods} = lists:foldl(F, {0, [], []}, DropList),
    %% 玩家掉落限制
    PersonLimits = calc_person_drop_limit(Ps, TCoinNum, TMyGoods),
    %% 全服掉落限制
    SerLimits = calc_all_server_drop_limit(TSerGoods),
    %% {个人玩家已获得数量列表, 全副已经掉落的物品数量, 金币限制数量}
    {PersonLimits, SerLimits, 0, 0}.

%% 计算掉落限制
do_handle_drop_goods_limit(Ps, DropList, {PersonLimits, SerLimits, DropedCoin, CoinLimit}) ->
    #player_status{scene=Scene,figure=#figure{lv=Lv}}=Ps,
    Day = util:get_open_day(),
    F = fun(Drop, {CoinNum, PMarkL, SMarkL, CanDropL})->
        #ets_drop_goods{drop_thing_type = DTType, goods_id = GoodsId, num = DNum} = Drop,
        if
            DTType == ?TYPE_GOODS ->
                case data_drop_limit:get_goods_limit(DTType, GoodsId) of
                    #base_drop_limit{limit_type = LimitType, limit_day = LimitDay, limit_num = LimitNum} when LimitNum > 0  ->
                        case  LimitType of
                            ?DROP_LIMIT_GOODID -> %% 玩家按单个物品限制数量
                                {NewPMarkL, NewCanDropL} = update_drop_limit_count(LimitDay, LimitNum, PMarkL, Drop, CanDropL),
                                {CoinNum, NewPMarkL, SMarkL, NewCanDropL};
                            
                            ?DROP_LIMIT_ID_SERVER ->
                                {NewSMarkL, NewCanDropL} = update_drop_limit_count(LimitDay, LimitNum, SMarkL, Drop, CanDropL),
                                {CoinNum, PMarkL, NewSMarkL, NewCanDropL};
                            _ ->
                                {CoinNum, PMarkL, SMarkL, CanDropL}
                        end;
                    _ ->
                        LimitNum =  data_exp_sea:get_sea_limit(GoodsId,Lv,Day),
                        ExpSea = exp_sea_logic:is_exp_sea_scene(Scene),
                        case LimitNum>0 of
                            true when ExpSea == true->
                                {NewPMarkL, NewCanDropL} = update_drop_limit_count(?LIMIT_DAY_1, LimitNum, PMarkL, Drop, CanDropL),
                                {CoinNum, NewPMarkL, SMarkL, NewCanDropL};
                            _->
                                {CoinNum, PMarkL, SMarkL, [Drop|CanDropL]}
                        end
                end;
            DTType== ?TYPE_COIN ->
                if
                    CoinLimit == 0 -> {CoinNum, PMarkL, SMarkL, [Drop|CanDropL]};
                    CoinNum >= CoinLimit -> {CoinNum, PMarkL, SMarkL, CanDropL};
                    true ->
                        {NCoinNum, NDrop} = ?IF(DNum + CoinNum < CoinLimit, {CoinNum + DNum, Drop},
                            {CoinLimit, Drop#ets_drop_goods{num = CoinLimit - CoinNum}}),
                        {NCoinNum, PMarkL, SMarkL, [NDrop|CanDropL]}
                end;
            true ->
                {CoinNum, PMarkL, SMarkL, CanDropL}
        end
        end,
    {NewCoin, NewPL, NewSL, Drops} = lists:foldl(F, {DropedCoin, PersonLimits, SerLimits, []}, DropList),
    {NewCoin, NewPL, NewSL, Drops, DropedCoin}.

%% 更新上限数量
update_drop_limit_count(LimitDay, LimitNum, MarkL, Drop, CanDropL) ->
    #ets_drop_goods{goods_id = GoodsId, num = DNum} = Drop,
    case lists:keyfind(LimitDay, 1, MarkL) of
        false -> {MarkL, CanDropL};
        {_LimitDay, MarkDropedL} ->
            case lists:keyfind(?DropKey(GoodsId), 1, MarkDropedL) of
                false ->
                    NewMarkDropedL = lists:keystore(?DropKey(GoodsId), 1, MarkDropedL, {?DropKey(GoodsId), DNum}),
                    NewMarkL = lists:keystore(LimitDay, 1, MarkL, {LimitDay, NewMarkDropedL}),
                    {NewMarkL, [Drop|CanDropL]};
                {_Key, DropedCount} when DropedCount >= LimitNum -> {MarkL, CanDropL};
                {Key, DropedCount} ->
                    if
                        DropedCount+DNum >= LimitNum ->
                            NewMarkDropedL = lists:keystore(Key, 1, MarkDropedL, {Key, LimitNum}),
                            NewMarkL = lists:keystore(LimitDay, 1, MarkL, {LimitDay, NewMarkDropedL}),
                            NNUM = LimitNum - DropedCount,
                            {NewMarkL, [Drop#ets_drop_goods{num = NNUM}|CanDropL]};
                        true ->
                            NewMarkDropedL = lists:keystore(Key, 1, MarkDropedL, {Key, DropedCount+DNum}),
                            NewMarkL = lists:keystore(LimitDay, 1, MarkL, {LimitDay, NewMarkDropedL}),
                            {NewMarkL, [Drop|CanDropL]}
                    end
            end
    end.

%% 更新玩家记录|全服记录
update_daily_drop_count(Ps, _CoinNum, PersonL, ServerL, _DropedCoin, PersonLimits, SerLimits) ->
    %% 玩家周一，周日
    RoleId = Ps#player_status.id,
    Fun = fun({?LIMIT_DAY_1, PL}) ->
        NewPL = compare_bf_limits(?LIMIT_DAY_1, PL, PersonLimits),
        lib_daily:set_count(RoleId, NewPL);
        ({?LIMIT_DAY_7, PL}) ->
            NewPL = compare_bf_limits(?LIMIT_DAY_7, PL, PersonLimits),
            lib_week:set_count(RoleId, NewPL);
        (_) -> skip
          end,
    [Fun(X) || X <- PersonL],
    %% 全服
    FS = fun({LimitDay, SL}) ->
        NewSL = compare_bf_limits(LimitDay, SL, SerLimits),
        mod_global_counter:set_count(NewSL)
         end,
    [FS(X) || X <- ServerL].

%% 获取全服掉落限制，已经掉落的次数
calc_all_server_drop_limit(TSerGoods)->
    Fun = fun({{GoodsId, LimitDay}, _Num}, Temp)->
        case lists:keyfind(LimitDay, 1, Temp) of
            false ->
                [{LimitDay, [{?MOD_GOODS, ?MOD_GOODS_DROP, GoodsId}]} | Temp];
            {_, GList} ->
                NewGList = [{?MOD_GOODS, ?MOD_GOODS_DROP, GoodsId}|GList],
                lists:keyreplace(LimitDay, 1, Temp, {LimitDay, NewGList})
        end
          end,
    Goods = lists:foldl(Fun, [], TSerGoods),
    %% 全局计算器获取全局掉落信息
    [{LimitDay, mod_global_counter:get_count(GoodsL, [{global_diff_day, LimitDay}])} || {LimitDay, GoodsL} <- Goods].

%% 计算玩家个人1天和7天掉落限制
calc_person_drop_limit(#player_status{id = Id}, _TCoinNum, Goods)->
    %% 增加一个橙装掉落上限
    Day1 = [{?MOD_GOODS, ?MOD_GOODS_DROP, GoodsId} || {{GoodsId, LimitDay}, _Num} <- Goods, LimitDay == ?LIMIT_DAY_1],
    Day7 = [{?MOD_GOODS, ?MOD_GOODS_DROP, GoodsId} || {{GoodsId, LimitDay}, _Num} <- Goods, LimitDay == ?LIMIT_DAY_7],
    [{?LIMIT_DAY_1, lib_daily:get_count_offline(Id, Day1)}, {?LIMIT_DAY_7, lib_week:get_count_offline(Id, Day7)}].

%% 过滤出不相同的次数
compare_bf_limits(LimitDay, L, Limits)->
    case lists:keyfind(LimitDay, 1, Limits) of
        false -> L;
        {_, OL} ->
            Fun = fun({Id, Count}=E, TL) ->
                case lists:keyfind(Id, 1, OL) of
                    {_, Count} -> TL;
                    _ -> [E|TL]
                end
                  end,
            lists:foldl(Fun, [], L)
    end.

%%%-------------------------------------------------------------------
%%% @doc
%%%     个人控制掉落上限
%%% @end
%%%-------------------------------------------------------------------
%% 特殊场景限制橙色碎片/紫装道具掉落次数
handle_color_goods_limit(_Player, MonArgs, DropRoles, TotalDropList0) ->
    #mon_args{scene = Scene} = MonArgs,
    case local_boss_api:is_home_boss_scene(Scene) of
        true ->
            %% 橙装碎片上限控制
            [{_, _, ChipLimit}|_] = data_local_boss:get_local_boss_kv(boss_home_limit),
            TotalDropList1 = color_goods_limit_do(DropRoles, TotalDropList0, orange_equipment_chip, ?GOODS_STUFF_SUBTYPE_EQUIPMENT_CHIP, ChipLimit),
            %% 紫装上限控制
            PurpleLimit = data_local_boss:get_local_boss_kv(boss_home_limit_purple),
            TotalDropList2 = color_goods_limit_do(DropRoles, TotalDropList1, purple_equipment, ?PURPLE, PurpleLimit),
            %% 进阶珍宝碎片
            FosterSkinLimit = 10,
            TotalDropList3 = color_goods_limit_do(DropRoles, TotalDropList2, foster_skin_chip, ?ORANGE, FosterSkinLimit),
            TotalDropList3;
        _ ->
            TotalDropList0
    end.

color_goods_limit_do(DropRoles, TotalDropList0, DropClass, GoodsType, Limit) ->
    case check_have_color_drop(TotalDropList0, DropClass) of
        ColorDrop = [_|_] ->
            DropRoleCounts =
                [begin
                     Count = lib_daily:get_count_offline(RoleId, ?MOD_GOODS, ?MOD_GOODS_HOME_DROP_COUNT, GoodsType),
                     {RoleId, Count} end || #mon_atter{id = RoleId} <- DropRoles],
            update_drop_goods_with_color_drop_limit(DropRoleCounts, Limit, ColorDrop, TotalDropList0);
        _ ->
            TotalDropList0
    end.


%% 检查是否有橙装碎片道具掉落
check_have_color_drop(DropList, Type) ->
    lists:filter(fun(#ets_drop_goods{goods_id = GoodsId}) ->
        case is_color_goods(GoodsId) of
            {true, Type} ->
                true;
            _ ->
                false
        end
    end, DropList).

update_drop_goods_with_color_drop_limit(DropRoleCounts, Limit, ColorDrop, DropList) ->
    DropRoleLen     = length(DropRoleCounts),
    DropRoleAllCount = lists:sum([Count || {_, Count} <- DropRoleCounts]),
    DropCountLimit  = DropRoleLen*Limit,
    AccessDropCount = max(0, DropCountLimit - DropRoleAllCount),
    {DropList1, _}  =
    lists:foldl(fun(Drop, {AccDropList, TempAccessDropCount}) ->
        #ets_drop_goods{id = DropId, num = GoodsNum} = Drop,
        if
            TempAccessDropCount =< 0 -> %% 橙色掉落次数没有了
                AccDropList1 = lists:keydelete(DropId, #ets_drop_goods.id, AccDropList),
                {AccDropList1, TempAccessDropCount};
            GoodsNum >= TempAccessDropCount -> %% 超过剩余可掉落数量了
                AccDropList1 = lists:keystore(DropId, #ets_drop_goods.id, AccDropList, Drop#ets_drop_goods{num = TempAccessDropCount}),
                {AccDropList1, 0};
            true ->
                {AccDropList, TempAccessDropCount - GoodsNum}
        end
    end, {DropList, AccessDropCount}, ColorDrop),
    DropList1.

%%%-------------------------------------------------------------------
%%% @doc
%%%     处理任务物品掉落
%%% @end
%%%-------------------------------------------------------------------
handle_item_task_event(_PS, _MonId, []) -> skip;
handle_item_task_event(PS, MonId, TaskGoods) ->
    %% 概率会掉的物品
    Goods = get_task_goods(TaskGoods, []),
    %% 去到任务进程再过滤没有相应任务的物品
    lib_role_task:send_drop_goods(PS#player_status.id,MonId,Goods).

%% 掉落物品过滤
get_task_goods([], List) -> List;
get_task_goods([#ets_drop_goods{goods_id = GoodsId, ratio = Ratio, bind = Bind}|T], List) ->
    Rand = urand:rand(1, ?RATIO_MAX),
    case Rand =< Ratio of
        true ->
            NewList = [{Bind*?TYPE_BIND_GOODS, GoodsId, 1} | List],
            get_task_goods(T, NewList);
        false ->
            get_task_goods(T, List)
    end.

%% task_goods_handle
filter_task_goods(RoleId, MonId, TaskGoods, Goods)->
    %% 判断物品是否有上限
    F = fun({Type, GoodsId, Num}, {TDayList, TList}) ->
        case lists:member(GoodsId, TaskGoods) of
            false -> {TDayList, TList};
            true ->
                case data_drop_limit:get_goods_limit(?TYPE_GOODS, GoodsId) of
                    #base_drop_limit{}-> {[{?MOD_GOODS, ?MOD_GOODS_DROP, GoodsId}|TDayList], TList};
                    _ -> {TDayList, [{Type, GoodsId, Num}|TList]}
                end
        end
        end,
    {DayLimitGoods, CanSendGoods} = lists:foldl(F, {[], []}, Goods),
    CanSendDayGoods
        = if
              DayLimitGoods =/= [] ->
                  case lib_daily:get_count(RoleId, DayLimitGoods) of
                      LimitCounts when is_list(LimitCounts)->
                          F1 = fun({{?MOD_GOODS, ?MOD_GOODS_DROP, GoodsId}, Count}, {DList, NDList}) ->
                              #base_drop_limit{limit_num = LimitNum}
                                  = data_drop_limit:get_goods_limit(?TYPE_GOODS, GoodsId),
                              case LimitNum > Count of
                                  false -> {DList, NDList};
                                  true ->
                                      {Type, GoodsId, _Num} = lists:keyfind(GoodsId, 2, Goods),
                                      {[{Type, GoodsId, 1}|DList], [{{?MOD_GOODS, ?MOD_GOODS_DROP, GoodsId}, Count+1}|NDList]}
                              end
                               end,
                          {SendDGoods, AddCounts} = lists:foldl(F1, {[], []}, LimitCounts),
                          lib_daily:set_count(RoleId, AddCounts),
                          SendDGoods;
                      _ ->
                          []
                  end;
              true ->
                  []
          end,
    if
        CanSendDayGoods =/= [] andalso CanSendGoods =/= [] ->
            skip;
        true ->
            SendGoods = lists:append(CanSendDayGoods, CanSendGoods),
            lib_goods_api:send_reward_by_id(SendGoods, mon_drop, MonId, RoleId)
    end.

drop_to_goods_list(DropList) ->
    [
        trans_to_goods(Type, GoodsId, Num, Bind)
        || #ets_drop{drop_thing_type = Type, goods_id = GoodsId, num = Num, bind = Bind} <- DropList
    ].

ets_drop_to_goods_list(DropList) ->
    [
        trans_to_goods(Type, GoodsId, Num, Bind)
        || #ets_drop_goods{drop_thing_type = Type, goods_id = GoodsId, num = Num, bind = Bind} <- DropList
    ].

trans_to_goods(Type, GoodsId, Num, Bind) ->
    case Type of
        ?TYPE_COIN -> {?TYPE_COIN, ?GOODS_ID_COIN, Num};
        ?TYPE_GOODS when Bind > 0 -> {?TYPE_BIND_GOODS, GoodsId, Num};
        ?TYPE_BIND_GOODS  -> {?TYPE_BIND_GOODS, GoodsId, Num};
        ?TYPE_GOODS -> {?TYPE_GOODS, GoodsId, Num};
        _ -> {Type, 0, Num}
    end.

is_need_control(#mon_args{boss = Boss, origin = Origin}) ->
    Origin =:= ?DROP_ORIGIN_PERSONAL_BOSS orelse
    Origin =:= ?DROP_ORIGIN_CRUSADE_BOSS orelse
    Boss =:= ?MON_REWARD_BOSS orelse Boss =:= ?MON_DESERTED_BOSS.

is_personal_boss(#mon_args{origin = Origin}) ->
    Origin =:= ?DROP_ORIGIN_PERSONAL_BOSS.
is_deserted_boss(#mon_args{boss = Boss}) ->
    Boss =:= ?MON_DESERTED_BOSS.

%% 转换为掉落配置中的origin参数
to_drop_cfg_origin(#mon_args{boss = Boss, origin = Origin}) ->
    if
        Origin =:= ?DROP_ORIGIN_PERSONAL_BOSS -> ?DROP_PERSONAL_BOSS;
        Origin =:= ?DROP_ORIGIN_CRUSADE_BOSS  -> ?DROP_CRUSADE_BOSS;
        Boss =:= ?MON_REWARD_BOSS   -> ?DROP_REWARD_BOSS;
        Boss =:= ?MON_DESERTED_BOSS -> ?DROP_DESERTED_BOSS;
        true ->
            false
    end.
