%% --------------------------------
%% desc     :藏宝图模块
%% module   :lib_treasure_map.erl
%% author   :hh
%% time     :21/02/25
%% --------------------------------


-module (lib_treasure_map).

-export ([
    init/1
    ,check/1
    ,use_treasure_map/2
    ,batch_use_normal_treasure_map/2
    ,get_treasure/3
    ,send_treasure_reward/2
    ,calc_treasure_rewards/4
    ,reset_treasure_map/2

    ,get_quiz/1
    ,answer_quiz/3
    ,get_treasure_shop_info/1
    ,treasure_shopping/4
    ,treasure_mon/1

    ,limit_shop_info_after_event/1
    ,buy_limit_shop/5
    ,handle_event/2
    ]).

-include ("server.hrl").
-include ("figure.hrl").
-include ("def_module.hrl").
-include ("rec_event.hrl").
-include ("def_event.hrl").
-include ("def_fun.hrl").
-include ("predefine.hrl").
-include ("treasure_map.hrl").
-include ("errcode.hrl").
-include ("common.hrl").
-include ("def_goods.hrl").
-include ("mon.hrl").
-include("scene.hrl").

-compile(export_all).

init(RoleId) ->
    DataMap1 = normal_treasure_init(RoleId),
    DataMap2 = lib_treasure_map_advance:login_init(RoleId, DataMap1),
    #role_treasure_map{map = DataMap2}.

normal_treasure_init(RoleId) ->
    case db:get_all(?SQL_SELECT_TREASURE_SHOP_INFO, [RoleId]) of
        Data = [_|_] ->
            normal_treasure_init_1(RoleId, Data);
        _ ->
            #{?NORMAL_TREASURE => #normal_map{}}
    end.

normal_treasure_init_1(RoleId, Data) ->
    Now = utime:unixtime(),
    {ShopList, DBList} =
    lists:foldl(fun([Time, LimitShopsBin|_], {TempShopList, ChangeDBList}) -> 
        if Now >= Time ->
                {TempShopList, [Time|ChangeDBList]};
            true ->
                LimitShops = util:bitstring_to_term(LimitShopsBin),
                {[{Time, LimitShops}|TempShopList], ChangeDBList}
        end
    end, {[], []}, Data),
    db_delete_expire_shop_info(RoleId, DBList),
    DataMap = update_map(#{}, [{limit_shop_info, ShopList}]),
    DataMap.

db_delete_expire_shop_info(_RoleId, []) ->
    ok;
db_delete_expire_shop_info(RoleId, DBList) ->
    case lists:reverse(lists:sort(DBList)) of
        [MaxTime|_] ->
            db:execute(?SQL_DELETE_TREASURE_SHOP_INFO, [RoleId, MaxTime]);
        _ ->
            ok
    end.

db_replace_limit_shop_info(RoleId, ExpireTime, NewLimitShops) ->
    db:execute(?SQL_REPLACE_TREASURE_SHOP_INFO, [RoleId, ExpireTime, util:term_to_bitstring(NewLimitShops)]),
    ok.

%% 中途下线处理
handle_event(Player, #event_callback{type_id = ?EVENT_DISCONNECT}) when is_record(Player, player_status) ->
   #player_status{role_treasure_map = RoleTreasureMap} = Player,
   #role_treasure_map{target = Target, time = Time, type = Type, map = Map} = RoleTreasureMap,
   NewPlayer =
   if Target =/= [] andalso Type == ?NORMAL_TREASURE andalso Time =/= 0 ->
           #normal_map{event_id = NormalEvent} = maps:get(?NORMAL_TREASURE, Map, #normal_map{}),
            if 
                NormalEvent == ?TREASURE_QUIZ 
                orelse NormalEvent == ?TREASURE_SHOP ->
                    half_logout_logic(Player, NormalEvent);
                true ->
                    Player
            end;
        true ->
            Player
   end,
   {ok, NewPlayer};

handle_event(Player, #event_callback{type_id = ?EVENT_DROP_CHOOSE, data = Maps}) when is_record(Player, player_status) ->
    #player_status{figure = #figure{name = RoleName}} = Player,
    MonId = data_treasure_map:get_kv(mon_id),
    case maps:get(mon_id, Maps, 0) of
        MonId ->
            case maps:get(goods, Maps, []) of
                [{_, GoodsId, Num}|_] ->
                    lib_chat:send_TV({all}, ?MOD_TREASURE_MAP, 5, [RoleName, GoodsId, Num]);
                _ ->
                    ok
            end;
        _ ->
            ok
    end,
    {ok, Player};

handle_event(_Player, _) ->
    {ok, _Player}.

%% 中途下线逻辑
half_logout_logic(Player, ?TREASURE_QUIZ) ->
    case answer_quiz(Player, 0, 0) of
        {ok, NewPlayer} -> NewPlayer;
        _               -> Player
    end;
%% 中途下线先把免费的发了
half_logout_logic(Player, ?TREASURE_SHOP) ->
    #player_status{role_treasure_map = RoleTreasureMap} = Player,
    Type = ?NORMAL_TREASURE,
    CheckList = [
        {is_in_treasure, RoleTreasureMap, ?NORMAL_TREASURE}
        ,{normal_event_check, ?TREASURE_SHOP, RoleTreasureMap}
        ,{is_enough_cost, Player, Type}
    ],
    case lib_treasure_map:check(CheckList) of
        true ->
            #role_treasure_map{map = Map} = RoleTreasureMap,
            #normal_map{limit_shop_info = AllLimitShopInfo} = TypeMap = maps:get(Type, Map, #normal_map{}),
            case treasure_free_shopping(Player) of
                {ok, NewPlayer} ->
                    NewPlayer1 = NewPlayer;
                {false, _Errcode} ->
                    NewPlayer1 = Player
            end,
            util:cancel_timer(TypeMap#normal_map.force_ref),
            %% 检查原有的限时付费奖励
            UnFinLimitShops = calc_unfin_limit_shop(TypeMap),
            ShopExpireTime  = maps:get(expire_time, TypeMap#normal_map.event_data, 0),
            %% 归纳到未过期仍可购买的付费奖励列表
            if UnFinLimitShops == [] ->
                    NewPlayer1;
                true ->
                    NewPlayer2 = calc_all_unfin_limit_shop_info(Type, ShopExpireTime, UnFinLimitShops, AllLimitShopInfo, NewPlayer1),
                    NewPlayer2
            end;
        {false, _Errcode} ->
            Player
    end.

%% 超时检查事件状态
normal_event_force_close(Player, ?TREASURE_QUIZ) ->
    #player_status{role_treasure_map = RoleTreasureMap} = Player,
    Type = ?NORMAL_TREASURE,
    CheckList = [
        {is_in_treasure, RoleTreasureMap, ?NORMAL_TREASURE}
        ,{is_enough_cost, Player, Type}
    ],
    case check(CheckList) of
        true ->
            case answer_quiz(Player, 0, 0) of
                {ok, NewPlayer} -> {ok, NewPlayer};
                _A              -> {ok, Player}
            end;
        {false, _Errcode} -> {ok, Player}
    end;
normal_event_force_close(Player, ?TREASURE_SHOP) ->
    #player_status{role_treasure_map = RoleTreasureMap} = Player,
    Type = ?NORMAL_TREASURE,    
    CheckList = [
        {is_in_treasure, RoleTreasureMap, ?NORMAL_TREASURE}
    ],
    case check(CheckList) of
        true ->
            %% 先发放限时免费奖励
            case treasure_free_shopping(Player) of
                {ok, NewPlayer} -> {true, NewPlayer};
                _               -> NewPlayer = Player
            end,
            #role_treasure_map{map = Map} = RoleTreasureMap,
            #normal_map{limit_shop_info = AllLimitShopInfo} = TypeMap = maps:get(Type, Map, #normal_map{}),
            % %% 检查原有的限时付费奖励
            UnFinLimitShops = calc_unfin_limit_shop(TypeMap),
            ShopExpireTime  = maps:get(expire_time, TypeMap#normal_map.event_data, 0),
            if  
                UnFinLimitShops == [] ->
                    {ok, NewPlayer};
                true ->
                    %% 归纳到未过期仍可购买的付费奖励列表
                    NewPlayer1 = calc_all_unfin_limit_shop_info(Type, ShopExpireTime, UnFinLimitShops, AllLimitShopInfo, NewPlayer),
                    {ok, NewPlayer1}
            end;
        {false, _Errcode} ->
            {ok, Player}
    end;
normal_event_force_close(Player, _) ->
    {noreply, Player}.

%% 珍宝之门扣除消耗并累计次数
treasure_shop_cost(Type, _TypeMap, Player) ->
    #player_status{id = RoleId, sid = Sid} = Player,
    % #normal_map{event_data = EventData} = TypeMap,
    EventCost = get_treasure_cost(Type),
    case lib_goods_api:cost_object_list_with_check(Player, EventCost, treasure_map_shop, "") of
        {true, NewPlayer, _} ->
            %% 更新次数 次数更新需要判断是否本次挖宝已加
            lib_daily:increment(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_TYPE_MAX, Type),            
            lib_daily:increment(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_DAILY_EVENT_COUNT),
            NewCount = lib_daily:get_count(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_TYPE_MAX, Type),
            lib_server_send:send_to_sid(Sid, pt_424, 42403, [NewCount]),
            {true, NewPlayer};
        {false, Errcode, _} ->
            {false, Errcode}
    end.

%% 检查本次珍宝事件还未买完的限时商品
calc_unfin_limit_shop(TypeMap) ->
    #normal_map{event_data = EventData} = TypeMap,
    ShopStatus  = maps:get(shop_status, EventData, 0),
    ShopInfoId  = maps:get(shop_info, EventData, 0),
    if ShopStatus == 0 andalso ShopInfoId =/= 0 ->
            RewardUseNumList = maps:get(reward_index_use_num, EventData, []),
            #base_treasure_map_door{rewards = Rewards1} = data_treasure_map:get_limit_shop_rewards(ShopInfoId),
            UnFinLimitShops =
            lists:foldl(fun(
                {Index, {{_GoodsType, _GoodsId, _GoodsNum}, _MoneyType, _Price, LimitNum}}, TempUnFinLimitShops) -> 
                case lists:keyfind({ShopInfoId, Index}, 1, RewardUseNumList) of
                    {_, OldUseNum} ->
                        if OldUseNum >= LimitNum ->
                                TempUnFinLimitShops;
                            true ->
                                [{{ShopInfoId, Index}, OldUseNum, LimitNum}|TempUnFinLimitShops]
                        end;
                    _ ->
                        [{{ShopInfoId, Index}, 0, LimitNum}|TempUnFinLimitShops]
                end
            end, [], Rewards1),
            UnFinLimitShops;
        true -> []
    end.

%% 归纳所有未买完的
calc_all_unfin_limit_shop_info(Type, ShopExpireTime, UnFinLimitShops, AllLimitShopInfo, Player) ->
    #player_status{id = RoleId, role_treasure_map = RoleTreasureMap} = Player,
    #role_treasure_map{map = NewDataMap} = RoleTreasureMap,
    NewTypeDataMap  = maps:get(Type, NewDataMap, #normal_map{}),
    % ExpireTime      = Time + ?SHOP_EXPIRE,
    LimitShopsBin   = util:term_to_bitstring(UnFinLimitShops),
    db:execute(?SQL_REPLACE_TREASURE_SHOP_INFO, [RoleId, ShopExpireTime, LimitShopsBin]),
    NewTypeDataMap1 = NewTypeDataMap#normal_map{limit_shop_info = [{ShopExpireTime, UnFinLimitShops}|AllLimitShopInfo]},
    NewNewDataMap1  = maps:put(Type, NewTypeDataMap1, NewDataMap),
    RemoveArgs  = [
        {remove, [shop_info, free_shop, free_shop_status, shop_status, reward_index_use_num, expire_time]}
        ,{event_id, 0}
        ,{start_time, 0}
    ],
    NewDataMap2 = update_map(NewNewDataMap1, RemoveArgs),
    NewRoleTreasureMap = RoleTreasureMap#role_treasure_map{map = NewDataMap2},
    NewRoleTreasureMap1 = reset_treasure_map(?NORMAL_TREASURE, NewRoleTreasureMap),
    NewPlayer1  = Player#player_status{role_treasure_map = NewRoleTreasureMap1},
    NewPlayer1.

%% 使用藏宝图
use_treasure_map(Player, Type) ->
    #player_status{id = RoleId, figure = #figure{vip = Vip}, role_treasure_map = RoleTreasureMap} = Player,
    {PosId, Scene, X, Y} = get_treasure_map_rand_xy(Player), %% 主城场景会强制分线,提前获取分线无意义
    EventId       = get_treasure_map_event(RoleId, Vip, Type),
    % EventId = ?TREASURE_SHOP,
    % ?ERR("~p", [EventId]),
    case scene_landform:is_block(Scene, X, Y) of
        false ->
            NewRoleTreasureMap = RoleTreasureMap#role_treasure_map{
                target = [{PosId, Scene, X, Y}],
                time   = utime:unixtime(),
                type   = Type
            },
            NewRoleTreasureMap1 = use_treasure_map_do(NewRoleTreasureMap, EventId, Type),
            NewPlayer = Player#player_status{role_treasure_map = NewRoleTreasureMap1},
            {ok, NewPlayer, {Scene, 0, X, Y, EventId}};
        _ ->
            {false, ?ERRCODE(err424_scene_block)}
    end.

use_treasure_map_do(RoleTreasureMap, EventId, ?NORMAL_TREASURE) ->
    #role_treasure_map{map = Map} = RoleTreasureMap,
    NewMap = update_map(Map, [{event_id, EventId}]),
    RoleTreasureMap#role_treasure_map{map = NewMap};
use_treasure_map_do(RoleTreasureMap, _, _) ->
    RoleTreasureMap.

batch_use_normal_treasure_map(BatchUse, Player) ->
    #player_status{id = RoleId, sid = Sid, role_treasure_map = RoleTreasureMap,
        figure = #figure{lv = RoleLv, vip = Vip}} = Player,
    CheckList = [
        {is_not_in_treasure_time, RoleTreasureMap, ?NORMAL_TREASURE} %% 是否处于藏宝阶段
        ,{is_enough_batch_use_cost, BatchUse, Player}     %% 是否有足够藏宝消耗
    ],
    case check(CheckList) of
        true ->
            NormalUseCount = lib_daily:get_count(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_TYPE_MAX, ?NORMAL_TREASURE),
            % TimesLimit = data_treasure_map:get_kv(map_times_max),
            [{GoodsType, GoodsId, GoodsNum}|_] = get_treasure_cost(?NORMAL_TREASURE),
            %% 每日限制能使用的次数
            % AccessCostNum = TimesLimit - NormalUseCount,
            % %% 目前拥有的物品数量
            % [{GoodsId, MaxNum}|_] = lib_goods_api:get_goods_num(Player, [GoodsId]),
            % NewCostNum = min(AccessCostNum, MaxNum),
            NewCostCount = umath:floor(BatchUse/GoodsNum),
            CostList = [{GoodsType, GoodsId, BatchUse}],
            case lib_goods_api:cost_object_list_with_check(Player, CostList, treasure_map_batch_cost, "") of
                {true, NewPlayer, _} ->
                    Rewards    = batch_calc_treasure_reward(RoleLv, Vip, NewCostCount),
                    NewPlayer1 = lib_goods_api:send_reward(NewPlayer, Rewards, treasure_map_batch_reward),
                    lib_daily:set_count(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_TYPE_MAX, ?NORMAL_TREASURE, NormalUseCount+NewCostCount),
                    {ok, BinData} = pt_424:write(42413, [?SUCCESS]),
                    {ok, BinData1} = pt_424:write(42403, [NormalUseCount+NewCostCount]),
                    lib_server_send:send_to_sid(Sid, <<BinData/binary, BinData1/binary>>),
                    {ok, NewPlayer1};
                {false, Errcode, _} ->
                    lib_server_send:send_to_sid(Sid, pt_424, 42413, [Errcode]),
                    ok
            end;
        {false, Errcode} ->
            lib_server_send:send_to_sid(Sid, pt_424, 42413, [Errcode]),
            ok
    end.

batch_calc_treasure_reward(RoleLv, Vip, Num) ->
    batch_calc_treasure_reward_1(RoleLv, Vip, Num, []).

batch_calc_treasure_reward_1(_RoleLv, _Vip, Num, Rewards) when Num =< 0 ->
    Rewards;
batch_calc_treasure_reward_1(RoleLv, Vip, Num, Rewards) ->
    {_, RewardId, _TvType} = calc_treasure_rewards(?NORMAL_TREASURE, RoleLv, Vip, 0),
    NewRewards =
    case data_treasure_map:get_reward(RewardId) of
        #base_treasure_map_rewards{rewards = OneReward} ->
            lists:append(OneReward, Rewards);
        _ ->
            Rewards
    end,
    batch_calc_treasure_reward_1(RoleLv, Vip, Num-1, NewRewards).

%% 收纳后限时未过期道具信息
limit_shop_info_after_event(Player) ->
    #player_status{sid = Sid, id = RoleId, role_treasure_map = RoleTreasureMap} = Player,
    #role_treasure_map{map = Map} = RoleTreasureMap,
    Type = ?NORMAL_TREASURE,
    TypeMap = maps:get(Type, Map, #normal_map{}),
    IsInTreasure = check([{is_in_treasure, RoleTreasureMap, Type}]),
    if
        IsInTreasure == {false, 4240012} orelse
         IsInTreasure == {false, err424_type_err} ->
            NewTypeMap = get_limit_shop_info_after_event(RoleId, Sid, TypeMap),
            NewRoleTreasureMap = RoleTreasureMap#role_treasure_map{map = maps:put(?NORMAL_TREASURE, NewTypeMap, Map)},
            NewPlayer = Player#player_status{role_treasure_map = NewRoleTreasureMap},
            {ok, NewPlayer};
        true -> %% 还在活动中
            %% 检查原有的限时付费奖励
            UnFinLimitShops = calc_unfin_limit_shop(TypeMap),
            ShopExpireTime  = maps:get(expire_time, TypeMap#normal_map.event_data, 0),
            % ?ERR("~p ~p", [UnFinLimitShops, ShopExpireTime]),
            %% 归纳到未过期仍可购买的付费奖励列表
            if UnFinLimitShops == [] ->
                    get_limit_shop_info_after_event(RoleId, Sid, TypeMap),
                    ok;
                true ->
                    #player_status{role_treasure_map = NewRoleTreasureMap} = NewPlayer1 =
                     calc_all_unfin_limit_shop_info(Type, ShopExpireTime, UnFinLimitShops, TypeMap#normal_map.limit_shop_info, Player),
                    #role_treasure_map{map = NewMap} = NewRoleTreasureMap,
                    NewTypeMap1 = maps:get(Type, NewMap, #normal_map{}),
                    get_limit_shop_info_after_event(RoleId, Sid, NewTypeMap1),
                    {ok, NewPlayer1}
            end
    end.

get_limit_shop_info_after_event(RoleId, Sid, TypeMap) ->
    #normal_map{limit_shop_info = LimitShops} = TypeMap,
    Now = utime:unixtime(),
    {NewLimitShops, ExpireTimeShops} = lists:foldl(
        fun({ExpireTime, UnFinLimitShops}, {TempLimitShops, TempExpireShops}) -> 
            if Now >= ExpireTime ->
                    {TempLimitShops, [ExpireTime|TempExpireShops]};
                true -> %% Now < ExpireTime
                    {[{ExpireTime, UnFinLimitShops}|TempLimitShops], TempExpireShops}
            end
    end, {[], []}, LimitShops),
    ExpireTimeShops =/= [] andalso db_delete_expire_shop_info(RoleId, ExpireTimeShops),
    NewTypeMap = TypeMap#normal_map{limit_shop_info = NewLimitShops},
    PackShopInfo = pack_limit_shop_info(NewLimitShops),
    % ?ERR("~p", [PackShopInfo]),
    lib_server_send:send_to_sid(Sid, pt_424, 42414, [?SUCCESS, PackShopInfo]),
    NewTypeMap.

%% 购买收纳得限时未过期道具
buy_limit_shop(Player, ExpireTime, RewardId, Index, BuyNum) ->
    #player_status{id = RoleId, sid = Sid, role_treasure_map = RoleTreasureMap} = Player,
    #role_treasure_map{map = Map} = RoleTreasureMap,
    TypeMap = maps:get(?NORMAL_TREASURE, Map, #normal_map{}),
    Now = utime:unixtime(),
    if Now >= ExpireTime ->
            lib_server_send:send_to_sid(Sid, pt_424, 42415, [?ERRCODE(err424_shop_expire), 0, 0, 0, 0, 0]),
            ok;
        true ->
            #normal_map{limit_shop_info = LimitShops} = TypeMap,
            ShopRet = lists:keyfind(ExpireTime, 1, LimitShops),
            if 
                not ShopRet ->
                    lib_server_send:send_to_sid(Sid, pt_424, 42415, [?ERRCODE(err424_not_exist_shop_info), 0, 0, 0, 0, 0]),
                    ok;
                true ->     
                    {_, UnFinLimitShops} = ShopRet,
                    UnFinShopRet = lists:keyfind({RewardId, Index}, 1, UnFinLimitShops),
                    if 
                        not UnFinShopRet ->
                            lib_server_send:send_to_sid(Sid, pt_424, 42415, [?ERRCODE(err424_not_exist_shop_info), 0, 0, 0, 0, 0]),
                            ok;
                        true ->                            
                            {_, OldUseNum, LimitNum} = UnFinShopRet,
                            if OldUseNum < LimitNum ->
                                    NewUseNum = BuyNum + OldUseNum,
                                    if 
                                        NewUseNum =< LimitNum andalso BuyNum > 0 ->
                                            #base_treasure_map_door{rewards = Rewards} = data_treasure_map:get_limit_shop_rewards(RewardId),
                                            {true, CostL, RewardsList, _NewBuyNum, _MaxNum} = calc_shop_goods_cost(Index, BuyNum, OldUseNum, Rewards),
                                            case lib_goods_api:cost_object_list_with_check(Player, CostL, treasure_limit_expire_shop_cost, "") of
                                                {true, NewPlayer, _} ->
                                                    NewPlayer1 = lib_goods_api:send_reward(NewPlayer, RewardsList, treasure_limit_expire_shop_reward),
                                                    NewUnFinLimitShops = lists:keystore({RewardId, Index}, 1, UnFinLimitShops, {{RewardId, Index}, OldUseNum+BuyNum, LimitNum}),
                                                    NewLimitShops = lists:keystore(ExpireTime, 1, LimitShops, {ExpireTime, NewUnFinLimitShops}),
                                                    db_replace_limit_shop_info(RoleId, ExpireTime, NewUnFinLimitShops),
                                                    NewMap = maps:put(?NORMAL_TREASURE, TypeMap#normal_map{limit_shop_info = NewLimitShops}, Map),
                                                    NewRoleTreasureMap = RoleTreasureMap#role_treasure_map{map = NewMap},
                                                    NewPlayer2 = NewPlayer1#player_status{role_treasure_map = NewRoleTreasureMap},
                                                    RestNum = max(0, LimitNum - (OldUseNum+BuyNum)),
                                                    lib_server_send:send_to_sid(Sid, pt_424, 42415, [?SUCCESS, ExpireTime, RewardId, Index, RestNum, LimitNum]),
                                                    {ok, NewPlayer2};
                                                {false, Errcode, _} ->
                                                    lib_server_send:send_to_sid(Sid, pt_424, 42415, [Errcode, 0, 0, 0, 0, 0]),
                                                    ok
                                            end;
                                        true ->
                                            lib_server_send:send_to_sid(Sid, pt_424, 42415, [?ERRCODE(err424_buy_num_limit), 0, 0, 0, 0, 0]),
                                            ok
                                    end;
                                true ->
                                    lib_server_send:send_to_sid(Sid, pt_424, 42415, [?ERRCODE(err424_shop_reward_fin), 0, 0, 0, 0, 0]),
                                    ok
                            end
                    end
            end
    end.

pack_limit_shop_info(LimitShops) ->
    lists:foldl(fun({ExpireTime, OneLimitShops}, Acc) ->
        lists:foldl(fun({{RewardId, Index}, UseNum, LimitNum}, TempAcc) -> 
            [{ExpireTime, RewardId, Index, max(0, LimitNum - UseNum), LimitNum}|TempAcc]
        end, Acc, OneLimitShops)
    end, [], LimitShops).

get_treasure_cost(?NORMAL_TREASURE) ->
    data_treasure_map:get_kv(cost_common_map);
get_treasure_cost(?ADVANCE_TREASURE) ->
    data_treasure_map:get_kv(cost_advanced_map).

%% 随机本次事件
get_treasure_map_event(RoleId, Vip, ?NORMAL_TREASURE) ->
    EventRateList = data_treasure_map:get_kv(event_rate),
    EventMax      = data_treasure_map:get_kv(event_max),
    EventCount    = lib_daily:get_count(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_DAILY_EVENT_COUNT),
    EventRate     = calc_vip_event_rate(Vip, EventRateList),
    if EventCount >= EventMax ->
            ?TREASURE_MONOPOLY;
        true ->
            Rand = urand:rand(1, 100),
            if Rand =< EventRate -> %% 触发秘闻
                    EventTypesRate = data_treasure_map:get_kv(event_type_rate),
                    case urand:rand_with_weight(EventTypesRate) of
                        EventId when not is_boolean(EventId) ->
                            EventId;
                        _ -> ?TREASURE_MONOPOLY
                    end;
                true -> ?TREASURE_MONOPOLY
            end
    end;
get_treasure_map_event(_, _, ?ADVANCE_TREASURE) ->
    ?TREASURE_TURNTABLE.

calc_vip_event_rate(Vip, EventRateList) ->
    case lists:reverse(lists:keysort(1, EventRateList)) of
        [{_MaxVip, Rate}|_] ->
            calc_vip_event_rate_do(Vip, EventRateList, Rate);
        _ -> %% 配置问题,默认相同概率
            50
    end.

calc_vip_event_rate_do(_, [], Rate) -> Rate;
calc_vip_event_rate_do(Vip, [{CfgVip,CfgRate}|Rest], Rate) ->
    if Vip =< CfgVip ->
            CfgRate;
        true ->
            calc_vip_event_rate_do(Vip, Rest, Rate)
    end.

get_treasure_map_rand_xy(Player) ->
    SameRate = data_treasure_map:get_kv(map_scene_same),
    Rand = urand:rand(1, 100),
    ClsOpenRet = czone_api:zone_enter_check_city(),
    PosIds     = data_treasure_map:get_all_pos(),
    PosIds1    = calc_access_pos(ClsOpenRet, PosIds),
    if Rand =< SameRate ->
            #player_status{role_treasure_map = #role_treasure_map{last_scene_info = LastSceneInfo}} = Player,
            case LastSceneInfo of
                [{PosId, Scene, X, Y}|_] -> ok;
                _ ->
                    {PosId, Scene, X, Y} = calc_treasure_map_rand_xy(ClsOpenRet, PosIds1)
            end;
        true ->
            {PosId, Scene, X, Y} = calc_treasure_map_rand_xy(ClsOpenRet, PosIds1)
    end,
    case lib_scene:check_enter(Player, 0, Scene, ?TRANS_TYPE_WORLD) of
        {true, _, _}  ->
            {PosId, Scene, X, Y};
        {false, _Error} ->
            get_treasure_map_rand_xy_1(Player, ClsOpenRet, PosIds1, 1)
    end.

get_treasure_map_rand_xy_1(_Player, _ClsOpenRet, _PosIds, Index) when Index >= 10 ->
    {0, 0, 0, 0};
get_treasure_map_rand_xy_1(Player, ClsOpenRet, PosIds, Index) ->
    {PosId, Scene, X, Y} = calc_treasure_map_rand_xy(ClsOpenRet, PosIds),
    case lib_scene:check_enter(Player, 0, Scene, ?TRANS_TYPE_WORLD) of
        {true, _, _} ->
            {PosId, Scene, X, Y};
        {false, _Error} ->
            get_treasure_map_rand_xy_1(Player, ClsOpenRet, PosIds, Index+1)
    end.

calc_treasure_map_rand_xy(_ClsOpenRet, PosIds) ->
    calc_treasure_map_rand_xy_1(_ClsOpenRet, PosIds, 1).

calc_treasure_map_rand_xy_1(_ClsOpenRet, _PosIds, Index) when Index >= 10 ->
    {0, 0, 0, 0};
calc_treasure_map_rand_xy_1(_ClsOpenRet, PosIds, Index) ->
    RandPosId  = urand:list_rand(PosIds),
    {Scene, X, Y} = data_treasure_map:get_pos(RandPosId),
    case scene_landform:is_block(Scene, X, Y) of
        true ->
            calc_treasure_map_rand_xy_1(_ClsOpenRet, PosIds, Index+1);
        _ ->
            {RandPosId, Scene, X, Y}
    end.
    % case scene_mgr:get_pool_scope(Scene) of
    %     {MinPool, _MaxPool} ->
    %         {Scene, MinPool, X, Y};
    %     _ ->
    %       calc_treasure_map_rand_xy(ClsOpenRet, PosIds, Index+1)  
    % end.

calc_access_pos(true, PosIds) ->
    PosIds;
calc_access_pos(false, PosIds) ->
    lists:foldl(fun(PosId, Acc) -> 
        case data_treasure_map:get_pos(PosId) of
            {Scene, _X, _Y} ->
                case lib_scene:is_clusters_scene(Scene) of
                    true -> Acc;
                    _    -> [PosId|Acc]
                end;
            _ -> Acc
        end
    end, [], PosIds).

%% 获取本次随机题目
get_treasure_map_rand_quiz() ->
    AllQuiz = data_treasure_map:get_all_quiz(),
    urand:list_rand(AllQuiz).

%% 获取生成怪物参数
get_treasure_mon_args(RoleId, _Scene) ->
    CreateKey = {?MOD_TREASURE_MAP, config:get_server_id(), RoleId},
    CallbackMfa = {?MODULE, treasure_mon_die_callback, []},
    Alive = data_treasure_map:get_kv(mon_alive),
    [{create_key, CreateKey}, {die_handler, CallbackMfa}, {stop, Alive*1000}].

%% 获取本次限时之门商品
calc_treasure_shop_info(Lv) ->
    AllShopIds = data_treasure_map:get_all_shop_rewards(),
    AllShopIds1 =
    lists:foldl(fun(ShopId, Acc) -> 
        case data_treasure_map:get_limit_shop_rewards(ShopId) of
            #base_treasure_map_door{min_lv = MinLv, max_lv = MaxLv, weight = Weight}
             when Lv >= MinLv andalso Lv =< MaxLv ->
                [{Weight, ShopId}|Acc];
            _ ->
                Acc
        end
    end, [], AllShopIds),
    RandShopId = urand:rand_with_weight(AllShopIds1),
    % Ret = data_treasure_map:get_limit_shop_rewards(RandShopId),
    RandShopId.

%% 获取本次藏宝奖励展示
get_treasure(Player, ?NORMAL_TREASURE, ?TREASURE_MONOPOLY) ->
    #player_status{role_treasure_map = RoleTreasureMap, figure = #figure{lv = RoleLv, vip = Vip}} = Player,
    #role_treasure_map{map = Map} = RoleTreasureMap,
    {ShowRewardIdL, RewardId, TvType} = calc_treasure_rewards(?NORMAL_TREASURE, RoleLv, Vip, 0),
    NewMap = update_map(Map, [{add, [{rewards, RewardId}]}, {tv_type, TvType}]),
    NewPlayer = Player#player_status{role_treasure_map = RoleTreasureMap#role_treasure_map{map = NewMap}},
    {ok, NewPlayer, RewardId, ShowRewardIdL};

get_treasure(Player, ?ADVANCE_TREASURE, _EventId) ->
    {NewPlayer, RewardId, ShowRewardIdL} = lib_treasure_map_advance:get_advanced_map(Player),
    {ok, NewPlayer, RewardId, ShowRewardIdL};

get_treasure(_Player, _, _) ->
    {false, ?FAIL}.

%% 发奖励
send_treasure_reward(Player, ?NORMAL_TREASURE) ->
    #player_status{role_treasure_map = RoleTreasureMap, figure = #figure{name = RoleName}} = Player,
    #role_treasure_map{map = Map} = RoleTreasureMap,
    #normal_map{event_data = EventData, tv_type = _TvType} = maps:get(?NORMAL_TREASURE, Map, #normal_map{}),
    RewardId = maps:get(rewards, EventData, []),
    case data_treasure_map:get_reward(RewardId) of
        #base_treasure_map_rewards{rewards = Rewards} ->
            NewPlayer   = lib_goods_api:send_reward(Player, Rewards, normal_treasure_monopoly),
            NewMap      = update_map(Map, [{remove, [rewards]}, {tv_type, 0}, {event_id, 0}]),
            NewPlayer1  = NewPlayer#player_status{role_treasure_map = RoleTreasureMap#role_treasure_map{map = NewMap}},
            %% TODO LOG && TV
            case Rewards of
                [{_, GoodsId, GoodsNum}|_] ->
                    spawn(fun() -> 
                        timer:sleep(3000),
                        lib_chat:send_TV({all}, ?MOD_TREASURE_MAP, 1, [RoleName, GoodsId, GoodsNum]) 
                    end);
                _ ->
                    skip
            end,
            {ok, NewPlayer1};
        _ ->
            {false, ?FAIL}
    end;

send_treasure_reward(Player, ?ADVANCE_TREASURE) ->
    case lib_treasure_map_advance:receive_advanced_map_reward(Player) of
        {ok, NewPlayer} ->
            {ok, NewPlayer};
        Error ->
            Error
    end;

send_treasure_reward(_Player, _) ->
    {false, ?FAIL}.

%% 获取答题信息
get_quiz(Player) ->
    #player_status{sid = Sid, figure = #figure{lv = Lv, vip = Vip}, role_treasure_map = RoleTreasureMap} = Player,
    #role_treasure_map{map = Map} = RoleTreasureMap,
    QuizStart = utime:unixtime(),
    QuizId = get_treasure_map_rand_quiz(),
    QuizRewardId = calc_event_reward(Lv, Vip),
    Ref = erlang:send_after(?QUIT_TIME, self(), {'mod', ?MODULE, normal_event_force_close, [?TREASURE_QUIZ]}),
    UpdateArgs = [{add, [{quiz_id, QuizId}, {quiz_reward, QuizRewardId}]}, {start_time, QuizStart}, {ref, Ref}],
    NewMap = update_map(Map, UpdateArgs),
    NewRoleTreasureMap = RoleTreasureMap#role_treasure_map{map = NewMap},
    NewPlayer = Player#player_status{role_treasure_map = NewRoleTreasureMap},
    lib_server_send:send_to_sid(Sid, pt_424, 42407, [?SUCCESS, QuizId, QuizRewardId]),
    {ok, NewPlayer}.

%% 获取限时商城信息
get_treasure_shop_info(Player) ->
    #player_status{sid = Sid, figure = #figure{lv = Lv, vip = Vip}, role_treasure_map = RoleTreasureMap} = Player,
    #role_treasure_map{map = Map} = RoleTreasureMap,
    Type = ?NORMAL_TREASURE,
    TypeMap      = maps:get(Type, Map, #normal_map{}),
    CostRet      = treasure_shop_cost(Type, TypeMap, Player),
    case CostRet of
        {true, NewPlayer} ->
            ShopInfoId   = calc_treasure_shop_info(Lv),
            FreeRewardId = calc_event_reward(Lv, Vip),
            Ref = erlang:send_after(?QUIT_TIME, self(), {'mod', ?MODULE, normal_event_force_close, [?TREASURE_SHOP]}),
            ShopExpireTime = data_treasure_map:get_kv(goods_time),
            Now = utime:unixtime(),
            EndTime = Now + ShopExpireTime + 30,
            UpdateArgs = [
                {add, 
                    [{free_shop, FreeRewardId}, {free_shop_status, 0}, {shop_info, ShopInfoId}, {shop_status, 0}, {expire_time, EndTime}]
                },
                {start_time, Now}, {ref, Ref}
            ],  
            NewMap = update_map(Map, UpdateArgs),
            NewRoleTreasureMap = RoleTreasureMap#role_treasure_map{map = NewMap},
            NewPlayer1 = NewPlayer#player_status{role_treasure_map = NewRoleTreasureMap},
            % ?ERR("~p ~p ~p", [FreeRewardId, ShopInfoId, EndTime]),
            %% 通知前端
            lib_server_send:send_to_sid(Sid, pt_424, 42409, [?SUCCESS, FreeRewardId, ShopInfoId, EndTime]),
            {ok, NewPlayer1};
        {false, Errcode} ->
            lib_server_send:send_to_sid(Sid, pt_424, 42409, [Errcode, 0, 0, 0]),
            ok
    end.

%% 限时商城免费领取
treasure_free_shopping(Player) ->
    #player_status{role_treasure_map = RoleTreasureMap} = Player,
    #role_treasure_map{map = Map} = RoleTreasureMap,
    Type = ?NORMAL_TREASURE,
    #normal_map{event_data = EventData} = maps:get(Type, Map, #normal_map{}),
    FreeRewardId = maps:get(free_shop, EventData, 0),
    FreeShopStatus = maps:get(free_shop_status, EventData, 0),
    ShopStatus   = maps:get(shop_status, EventData, 0),
    if FreeShopStatus == 0 andalso
        FreeRewardId =/= 0 ->
            %% 免费商品
            #base_treasure_event_rewards{rewards = Rewards} = data_treasure_map:get_event_rewards(FreeRewardId),
            NewPlayer2 = lib_goods_api:send_reward(Player, Rewards, treasure_map_free_shop),
            NewMap     = update_map(Map, [{add, [{free_shop_status, 1}]}]),
            %% 限时商品数据状态处理
            if ShopStatus =/= 0 ->
                    RemoveArgs = [
                        {remove, [shop_info, free_shop, free_shop_status, shop_status, reward_index_use_num, expire_time]}
                        ,{event_id, 0}
                        ,{start_time, 0}
                    ],
                    NewMap1 = update_map(NewMap, RemoveArgs),
                    NewRoleTreasureMap = reset_treasure_map(Type, RoleTreasureMap#role_treasure_map{map = NewMap1});
                true ->
                    NewRoleTreasureMap = RoleTreasureMap#role_treasure_map{map = NewMap}
            end,
            NewPlayer3 = NewPlayer2#player_status{role_treasure_map = NewRoleTreasureMap},
            {ok, NewPlayer3};
        true ->
            {false, ?ERRCODE(err424_shop_reward_fin)}
    end.

%% 购买限时商城信息
treasure_shopping(Player, RewardId, Index, BuyNum) ->
    #player_status{sid = Sid, role_treasure_map = RoleTreasureMap} = Player,
    #role_treasure_map{map = Map} = RoleTreasureMap,
    Type = ?NORMAL_TREASURE,
    #normal_map{event_data = EventData} = maps:get(?NORMAL_TREASURE, Map, #normal_map{}),
    %% 获取旧使用次数
    UseNumList = maps:get(reward_index_use_num, EventData, []),
    case lists:keyfind({RewardId, Index}, 1, UseNumList) of
        {_, OldUseNum} -> ok;
        _ -> OldUseNum = 0
    end,
    %% 获取免费商店状态
    FreeShopStatus  = maps:get(free_shop_status, EventData, 0),
    %% 付费限时商品
    #base_treasure_map_door{rewards = Rewards2} = data_treasure_map:get_limit_shop_rewards(RewardId),
    {true, CostL, RewardsList2, NewBuyNum, MaxNum} = calc_shop_goods_cost(Index, BuyNum, OldUseNum, Rewards2),
    {true, NewPlayer, _} = lib_goods_api:cost_object_list_with_check(Player, CostL, treasure_map_shop, ""),
    NewPlayer1 = lib_goods_api:send_reward(NewPlayer, RewardsList2, treasure_map_shop),
    
    %% 更新购买使用次数
    NewUseNumList = lists:keystore({RewardId, Index}, 1, UseNumList, {{RewardId, Index}, OldUseNum+NewBuyNum}),
    %% 重置数据需要判断本次挖宝事件是否已结束
    ShopStatus = calc_limit_shopping_status(RewardId, Rewards2, NewUseNumList),
    NewMap = update_map(Map, [{add, [{reward_index_use_num, NewUseNumList}, {shop_status, ShopStatus}]}]),
    if ShopStatus =/= 0 andalso FreeShopStatus =/= 0 ->
            RemoveArgs = [
                {remove, [shop_info, shop_status, free_shop_status, reward_index_use_num, expire_time]}
                ,{event_id, 0}
                ,{start_time, 0}
            ],
            NewMap1 = update_map(NewMap, RemoveArgs),
            NewRoleTreasureMap = reset_treasure_map(Type, RoleTreasureMap#role_treasure_map{map = NewMap1});
        true ->
            NewRoleTreasureMap = RoleTreasureMap#role_treasure_map{map = NewMap}
    end,
    NewPlayer2 = NewPlayer1#player_status{role_treasure_map = NewRoleTreasureMap},
    RestNum = max(0, MaxNum - (OldUseNum+NewBuyNum)),
    lib_server_send:send_to_sid(Sid, pt_424, 42410, [?SUCCESS, Index, RestNum]),
    {ok, NewPlayer2}.

%% 0未完成|1已完成
calc_limit_shopping_status(RewardId, Rewards, UseNumList) ->
    case lists:any(fun({Index, {_, _MoneyType, _Price, LimitBuy}}) -> 
        case lists:keyfind({RewardId, Index}, 1, UseNumList) of
            {_, UseNum} ->
                if UseNum < LimitBuy -> true;
                    true             -> false
                end;
            _ ->
                true
        end
    end, Rewards) of
        true ->
            0;
        _ ->
            1
    end.

%% 答题
answer_quiz(Player, QuizId, AnswerId) ->
    #player_status{id = RoleId, sid = Sid, role_treasure_map = RoleTreasureMap} = Player,
    #role_treasure_map{map = Map} = RoleTreasureMap,
    RewardsRate = check_answer(QuizId, AnswerId),
    Type = ?NORMAL_TREASURE,
    CostList = lib_treasure_map:get_treasure_cost(Type),
    case lib_goods_api:cost_object_list_with_check(Player, CostList, treasure_map_cost, "") of
        {true, NewPlayer, _} ->
            #normal_map{event_data = EventData} = maps:get(?NORMAL_TREASURE, Map, #normal_map{}),
            RewardId    = maps:get(quiz_reward, EventData, 0),
            %% 发奖励
            case data_treasure_map:get_event_rewards(RewardId) of
                #base_treasure_event_rewards{rewards = Rewards} -> ok;
                _ -> Rewards = []
            end,
            Rewards1    = [{GoodsType, GoodsId, round(RewardsRate*GoodsNum)} || {GoodsType, GoodsId, GoodsNum} <- Rewards],
            NewPlayer1  = lib_goods_api:send_reward(NewPlayer, Rewards1, treasure_map_quiz_reward),
            %% 增加次数
            lib_daily:increment(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_TYPE_MAX, Type),
            lib_daily:increment(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_DAILY_EVENT_COUNT),
            %% 重置数据
            NewMap = update_map(Map, [{remove, [quiz_id, quiz_reward]}, {event_id, 0}, {start_time, 0}, {ref, undefined}]),
            NewRoleTreasureMap = reset_treasure_map(Type, RoleTreasureMap#role_treasure_map{map = NewMap}),
            NewPlayer2 = NewPlayer1#player_status{role_treasure_map = NewRoleTreasureMap},
            NewCount = lib_daily:get_count(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_TYPE_MAX, Type),
            {ok, BinData}  = pt_424:write(42408, [?SUCCESS]),
            {ok, BinData1} = pt_424:write(42403, [NewCount]),
            lib_server_send:send_to_sid(Sid, <<BinData/binary, BinData1/binary>>),
            {ok, NewPlayer2};
        {false, Errcode, _} ->
            lib_server_send:send_to_sid(Sid, pt_424, 42408, [Errcode]),
            ok
    end.

%% 触发怪物事件
treasure_mon(Player) ->
    #player_status{
        id = RoleId, sid = Sid, scene = Scene, scene_pool_id = ScenePool, copy_id = CopyId, 
        x = X, y = Y, role_treasure_map = RoleTreasureMap, figure = #figure{name = RoleName, lv = Lv, vip = Vip}} = Player,
    Type = ?NORMAL_TREASURE,
    CostList = lib_treasure_map:get_treasure_cost(Type),
    case lib_goods_api:cost_object_list_with_check(Player, CostList, treasure_map_cost, "") of
        {true, NewPlayer, _} ->
            #role_treasure_map{target = Target} = RoleTreasureMap,
            MonId = data_treasure_map:get_kv(mon_id),
            Args = get_treasure_mon_args(RoleId, Scene),
            lib_mon:async_create_mon(MonId, Scene, ScenePool, X, Y, 1, CopyId, 1, Args),
            EventRewardId = calc_event_reward(Lv, Vip),
            if EventRewardId =/= 0 ->
                    %% 发奖励
                    #base_treasure_event_rewards{rewards = Rewards} = data_treasure_map:get_event_rewards(EventRewardId),
                    NewPlayer1 = lib_goods_api:send_reward(NewPlayer, Rewards, treasure_map_mon_dicover);
                true ->
                    NewPlayer1 = NewPlayer
            end,
            %% 增加次数
            lib_daily:increment(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_TYPE_MAX, Type),
            lib_daily:increment(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_DAILY_EVENT_COUNT),
            % NewMap = update_map(Map, [{add, [{mon_id, MonId}]}, {start_time, utime:unixtime()}]),
            NewRoleTreasureMap = reset_treasure_map(Type, RoleTreasureMap),
            NewPlayer2 = NewPlayer1#player_status{role_treasure_map = NewRoleTreasureMap},
            NewCount = lib_daily:get_count(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_TYPE_MAX, Type),
            {ok, BinData1} = pt_424:write(42403, [NewCount]),
            {ok, BinData} = pt_424:write(42411, [?SUCCESS]),
            lib_server_send:send_to_sid(Sid, <<BinData/binary, BinData1/binary>>),
            case Target of
                [{PosId, _, _, _}|_] ->
                    lib_chat:send_TV({all}, ?MOD_TREASURE_MAP, 4, [RoleName, PosId]);
                _ -> ok
            end,
            {ok, NewPlayer2};
        {false, Errcode, _} ->
            lib_server_send:send_to_sid(Sid, pt_424, 42411, [Errcode]),
            ok
    end.

treasure_mon_die_callback(#ob_act{object = #scene_object{mon = #scene_mon{create_key = CreateKey, mid = MonId}}},
    KList, _Atter, _AtterSign, []) ->
    {?MOD_TREASURE_MAP, SvrId, CreateId} = CreateKey,
    treasure_mon_die_logic(KList, CreateId, SvrId, MonId),
    ok;
treasure_mon_die_callback(_, _, _, _, _) ->
    ok.

treasure_mon_die_logic(KList, CreateId, CreateSvr, MonId) ->
    NewKList = [{RoleId, SvrId} || #mon_atter{id = RoleId, server_id = SvrId} <- KList, RoleId =/= CreateId],
    IsHurt = lists:keymember(CreateId, #mon_atter.id, KList),
    creator_get_reward(CreateId, IsHurt, CreateSvr, MonId),
    hurt_list_get_reward(NewKList, MonId),
    ok.

%% 参与奖励-藏宝图普通奖励
hurt_list_get_reward(KList, MonId) ->
    ClsType = config:get_cls_type(),
    [begin 
        if ClsType == ?NODE_CENTER ->
                clusters_center:apply_cast(SvrId, ?MODULE, hurt_role_get_reward, [RoleId, MonId]);
            true ->
                hurt_role_get_reward(RoleId, MonId)
        end
    end || {RoleId, SvrId} <- KList].

hurt_role_get_reward(RoleId, MonId) ->
    Pid = misc:get_player_process(RoleId),
    case is_pid(Pid) andalso misc:is_process_alive(Pid) of
        true ->
            lib_player:apply_cast(RoleId, ?APPLY_CAST_SAVE, ?NOT_HAND_OFFLINE, ?MODULE, role_proc_hurt_role_get_reward, []);
        _ -> %% OFFLINE
            hurt_role_get_reward_offline(RoleId, MonId),
            ok
    end.    

%% 创怪者发现奖励-藏宝图秘闻奖励
creator_get_reward(CreateId, IsHurt, CreateSvr, MonId) ->
    ClsType = config:get_cls_type(),
    if ClsType == ?NODE_CENTER ->
            clusters_center:apply_cast(CreateSvr, ?MODULE, creator_get_reward_1, [CreateId, IsHurt, MonId]);
        true ->
            creator_get_reward_1(CreateId, IsHurt, MonId)
    end.

creator_get_reward_1(CreateId, IsHurt, MonId) ->
    Pid = misc:get_player_process(CreateId),
    case is_pid(Pid) andalso misc:is_process_alive(Pid) of
        true ->
            lib_player:apply_cast(CreateId, ?APPLY_CAST_SAVE, ?NOT_HAND_OFFLINE, ?MODULE, role_proc_create_get_reward, [IsHurt, MonId]);
        _ -> %% OFFLINE
            %% 离线处理
            creator_get_reward_offline(CreateId, IsHurt, MonId),
            ok
    end.

%% 参与击杀者处理
role_proc_hurt_role_get_reward(Player) ->
    #player_status{figure = #figure{lv = RoleLv, vip = Vip}} = Player,
    {_, RewardId, _} = calc_treasure_rewards(?NORMAL_TREASURE, RoleLv, Vip, 0),
    #base_treasure_map_rewards{rewards = Rewards} = data_treasure_map:get_reward(RewardId),
    NewPlayer   = lib_goods_api:send_reward(Player, Rewards, treasure_map_mon_hurt),
    {ok, NewPlayer}.

%% 创怪者处理-若参与击杀有参与奖励
role_proc_create_get_reward(Player, IsHurt, _MonId) ->
    #player_status{figure = #figure{lv = Lv, vip = Vip}} = Player,
    if IsHurt ->
            {_, RewardId, _} = calc_treasure_rewards(?NORMAL_TREASURE, Lv, Vip, 0),
            #base_treasure_map_rewards{rewards = Rewards} = data_treasure_map:get_reward(RewardId),
            NewPlayer = lib_goods_api:send_reward(Player, Rewards, treasure_map_mon_hurt),
            {ok, NewPlayer};
        true ->
            {ok, Player}
    end.

%% 参与者下线处理
hurt_role_get_reward_offline(RoleId, MonId) ->
    case lib_role:get_role_figure(RoleId) of
        #figure{lv = RoleLv, vip = Vip} ->
            {_, RewardId, _} = calc_treasure_rewards(?NORMAL_TREASURE, RoleLv, Vip, 0),
            #base_treasure_map_rewards{rewards = Rewards} = data_treasure_map:get_reward(RewardId),
            lib_goods_api:send_reward_with_mail(RoleId, Rewards, treasure_map_mon_hurt, integer_to_list(MonId));
        _ ->
            skip
    end,
    ok.

%% 创怪者下线处理-若参与击杀有奖励
creator_get_reward_offline(CreateId, IsHurt, _MonId) ->
    case lib_role:get_role_figure(CreateId) of
        #figure{lv = Lv, vip = Vip} when IsHurt ->
            {_, RewardId, _} = calc_treasure_rewards(?NORMAL_TREASURE, Lv, Vip, 0),
            #base_treasure_map_rewards{rewards = Rewards} = data_treasure_map:get_reward(RewardId),
            lib_goods_api:send_reward_with_mail(CreateId, Rewards, treasure_map_mon_hurt, "");
        _ ->
            skip
    end. 

update_map(Map, UpdateArgs) ->
    Type    = ?NORMAL_TREASURE,
    TypeMap = maps:get(Type, Map, #normal_map{}),
    NewTypeMap = update_type_map(TypeMap, UpdateArgs),
    NewMap  = maps:put(Type, NewTypeMap, Map),
    NewMap.

update_type_map(TypeMap, []) -> 
    TypeMap;

update_type_map(TypeMap, [{remove, KeyL}|Rest]) ->
    NewTypeMap = lists:foldl(fun(Key, TempTypeMap) ->
        #normal_map{event_data = EventData} = TempTypeMap,
        NewEventData = maps:remove(Key, EventData),
        TempTypeMap#normal_map{event_data = NewEventData}
    end, TypeMap, KeyL),
    update_type_map(NewTypeMap, Rest);

update_type_map(TypeMap, [{add, KeyVals}|Rest]) ->
    NewTypeMap = lists:foldl(fun({Key, Val}, TempTypeMap) ->
        #normal_map{event_data = EventData} = TempTypeMap,
        NewEventData = maps:put(Key, Val, EventData),
        TempTypeMap#normal_map{event_data = NewEventData}
    end, TypeMap, KeyVals),
    update_type_map(NewTypeMap, Rest);

update_type_map(TypeMap, [{event_id, EventId}|Rest]) ->
    NewTypeMap = TypeMap#normal_map{event_id = EventId},
    update_type_map(NewTypeMap, Rest);

update_type_map(TypeMap, [{limit_shop_info, LimitShopInfo}|Rest]) ->
    NewTypeMap = TypeMap#normal_map{limit_shop_info = LimitShopInfo},
    update_type_map(NewTypeMap, Rest);

update_type_map(TypeMap, [{tv_type, TvType}|Rest]) ->
    NewTypeMap = TypeMap#normal_map{tv_type = TvType},
    update_type_map(NewTypeMap, Rest);

update_type_map(TypeMap, [{start_time, Time}|Rest]) ->
    NewTypeMap = TypeMap#normal_map{start_time = Time},
    update_type_map(NewTypeMap, Rest);

update_type_map(TypeMap, [{ref, Ref}|Rest]) ->
    #normal_map{force_ref = OldRef} = TypeMap,
    util:cancel_timer(OldRef),
    NewTypeMap = TypeMap#normal_map{force_ref = Ref},
    update_type_map(NewTypeMap, Rest).

reset_treasure_map(Type = ?NORMAL_TREASURE, RoleTreasureMap) ->
    #role_treasure_map{target = Target, map = DataMap} = RoleTreasureMap,
    TypeMap = maps:get(Type, DataMap, #normal_map{}),
    util:cancel_timer(TypeMap#normal_map.force_ref),
    NewTypeMap = TypeMap#normal_map{
        event_id = 0,           %% 事件id
        start_time = 0,         %% 事件开始时间
        force_ref = undefined,  %% 强制关闭定时
        tv_type = 0,            %% 传闻类型
        event_data = #{}        %% 事件其余数据
    },
    RoleTreasureMap#role_treasure_map{
        type = 0, time = 0, target = [],
        map = maps:put(Type, NewTypeMap, DataMap),
        last_scene_info = Target
    };
reset_treasure_map(?ADVANCE_TREASURE, RoleTreasureMap) ->
    #role_treasure_map{target = Target} = RoleTreasureMap,
    RoleTreasureMap#role_treasure_map{
        type = 0, time = 0, target = [],
        last_scene_info = Target
    }.

check_answer(QuizId, AnswerId) ->
    case data_treasure_map:get_quiz(QuizId) of
        #base_treasure_map_quiz{answer = AnswerCfg} ->
            if AnswerCfg =/= AnswerId ->
                    1;
                true ->
                    2
            end;
        _ -> 1
    end.

calc_event_reward(Lv, Vip) ->
    EventIdRewards  = data_treasure_map:get_all_event_rewards(),
    EventIdRewards1 = lists:foldl(fun(RewardId, Acc) -> 
        case data_treasure_map:get_event_rewards(RewardId) of
            #base_treasure_event_rewards{min_lv = MinLv, max_lv = MaxLv, min_vip = MinVip, weight = Weight}
             when Lv >= MinLv andalso Lv =< MaxLv andalso Vip >= MinVip ->
                [{Weight, RewardId}|Acc];
            _ ->
                Acc
        end
    end, [], EventIdRewards),
    case urand:rand_with_weight(EventIdRewards1) of
        RandRewardId when RandRewardId =/= false ->
            RandRewardId;
        _ ->
            0
    end.

check([]) ->
    true;
check([{type_check, Type}|Rest]) ->
    if Type =/= ?NORMAL_TREASURE andalso Type =/= ?ADVANCE_TREASURE ->
            {false, ?ERRCODE(args_error)};
        true ->
            check(Rest)
    end;
check([{type_event_check, Type, EventId}|Rest]) ->
    IsEvent = lists:member(EventId, [?TREASURE_MONOPOLY, ?TREASURE_TURNTABLE]),
    TypeRet = Type =/= ?NORMAL_TREASURE andalso Type =/= ?ADVANCE_TREASURE,
    if 
        TypeRet ->
            {false, ?ERRCODE(args_error)};
        not IsEvent ->
            {false, ?ERRCODE(args_error)};
        true ->
            check(Rest)
    end;
check([{is_not_in_treasure_time, RoleTreasureMap, _Type}|Rest]) ->
    #role_treasure_map{type = NowType, map = DataMap} = RoleTreasureMap,
    if NowType == ?NORMAL_TREASURE ->
            TypeMap = maps:get(NowType, DataMap, #normal_map{}),
            #normal_map{start_time = StartTime} = TypeMap,
            if StartTime =/= 0 -> %% 事件已开始
                    {false, ?ERRCODE(err424_in_treasure_map)};
                true ->
                    check(Rest)
            end;
        true ->
            check(Rest)
    end;
check([{is_in_treasure, RoleTreasureMap, Type}|Rest]) ->
    #role_treasure_map{type = MyType, time = Time} = RoleTreasureMap,
    ExpireTime = Time + ?USE_EXPIRE_TIME,
    Now = utime:unixtime(),
    ExpireRet  = (Now >= ExpireTime andalso Time > 0) orelse Time == 0,
    if 
        MyType == 0 ->
            {false, ?ERRCODE(err424_not_in_treasure_map)};
        Type =/= MyType ->
            {false, ?ERRCODE(err424_type_err)};
        ExpireRet ->
            {false, ?ERRCODE(err424_not_in_treasure_map)};
        true -> check(Rest)
    end;
check([{is_enough_cost, Player, Type}|Rest]) ->
    CostList = get_treasure_cost(Type),
    case lib_goods_api:check_object_list(Player, CostList) of
        true ->
            check(Rest);
        {false, Errcode} ->
            {false, Errcode}
    end;
check([{times_limit, Type, RoleId}|Rest]) ->
    TimesLimit = data_treasure_map:get_kv(map_times_max),
    case lib_daily:get_count(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_TYPE_MAX, Type) of
        Times when Times >= TimesLimit -> {false, ?ERRCODE(err424_count_limit)};
        _ -> check(Rest)
    end;
check([{coordinate_check, Scene, X, Y, RoleTreasureMap}|Rest]) ->
    #role_treasure_map{target = Target} = RoleTreasureMap,
    case Target of
        [{_, Scene, TarX, TarY}|_] ->
            if abs(TarX - X) > 150 orelse abs(TarY - Y) > 150 ->
                    {false, ?ERRCODE(err424_distance_far_away)};
                true ->
                    check(Rest)
            end;
        _ ->
            {false, ?ERRCODE(err424_distance_far_away)}
    end;
check([{normal_event_check, EventId, RoleTreasureMap}|Rest]) ->
    #role_treasure_map{map = Map} = RoleTreasureMap,
    #normal_map{event_id = TypeEventId} = maps:get(?NORMAL_TREASURE, Map, #normal_map{}),
    if EventId == TypeEventId ->
            check(Rest);
        true ->
            {false, ?ERRCODE(err424_not_in_type_event)}
    end;
check([{is_quiz_event, _QuizId, RoleTreasureMap}|Rest]) ->
    #role_treasure_map{map = Map} = RoleTreasureMap,
    #normal_map{event_data = EventData, start_time = QuizStart} = maps:get(?NORMAL_TREASURE, Map, #normal_map{}),
    MapQuizId = maps:get(quiz_id, EventData, 0),
    Now = utime:unixtime(),
    QuizExpire = QuizStart + ?QUIT_TIME,
    Ret = QuizStart =/= 0 andalso QuizExpire > Now andalso MapQuizId =/= 0,
    if
        Ret ->
            check(Rest);
        true ->
            {false, ?ERRCODE(err424_not_in_quiz_event)}
    end;
check([{is_shop_event, [ExpectRet], RoleTreasureMap}|Rest]) ->
    #role_treasure_map{map = Map} = RoleTreasureMap,
    #normal_map{start_time = ShopStart} = maps:get(?NORMAL_TREASURE, Map, #normal_map{}),
    Ret = ShopStart =/= 0,
    if ExpectRet == Ret ->
            check(Rest);
        true ->
            {false, ?ERRCODE(err424_not_in_shop_event)}
    end;
check([{is_shop_event, [ExpectRet, Key], RoleTreasureMap}|Rest]) ->
    #role_treasure_map{map = Map} = RoleTreasureMap,
    #normal_map{event_data = EventData, start_time = ShopStart} = maps:get(?NORMAL_TREASURE, Map, #normal_map{}),
    KeyStatus = maps:get(Key, EventData, 0),
    Ret = ShopStart =/= 0 andalso KeyStatus == ExpectRet,
    if  
        Ret ->
            check(Rest);
        true ->
            if
                ShopStart == 0 ->
                    {false, ?ERRCODE(err424_not_in_shop_event)};
                true ->
                    {false, ?ERRCODE(err424_shop_reward_fin)}
            end
    end;
check([{is_mon_event, RoleTreasureMap}|Rest]) ->
    #role_treasure_map{map = Map} = RoleTreasureMap,
    #normal_map{event_id = EventId, start_time = MonStart, event_data = EventData} = maps:get(?NORMAL_TREASURE, Map, #normal_map{}),
    MonId = maps:get(mon_id, EventData, 0),
    if EventId =/= ?TREASURE_BOSS ->
            {false, ?ERRCODE(err424_not_in_mon_event)};
        MonId =/= 0 orelse MonStart =/= 0 ->
            {false, ?ERRCODE(err424_pls_fin_now_mon_event)};
        true ->
            check(Rest)
    end;
check([{is_enough_shop_cost, Player, Type, RewardId, Index, BuyNum}|Rest]) ->
    case check([{is_enough_cost, Player, Type}]) of
        true ->
            #player_status{role_treasure_map = RoleTreasureMap} = Player,
            #role_treasure_map{map = Map} = RoleTreasureMap,
            #normal_map{event_data = EventData} = maps:get(?NORMAL_TREASURE, Map, #normal_map{}),
            UseNumList = maps:get(reward_index_use_num, EventData, []),
            case lists:keyfind({RewardId, Index}, 1, UseNumList) of
                {_, OldUseNum} -> OldUseNum;
                _   -> OldUseNum = 0
            end,
            case data_treasure_map:get_limit_shop_rewards(RewardId) of
                #base_treasure_map_door{rewards = Rewards = [_|_]} ->
                    case calc_shop_goods_cost(Index, BuyNum, OldUseNum, Rewards) of
                        {true, CostL, _Rewards, _NewBuyNum, _} ->
                            case lib_goods_api:check_object_list(Player, CostL) of
                                true -> check(Rest);
                                {false, Errcode} -> {false, Errcode}
                            end;
                        {false, Errcode} -> {false, Errcode}
                    end;
                _ ->
                    {false, ?FAIL}
            end;
        {false, Errcode} ->
            {false, Errcode}
    end;
check([{is_enough_batch_use_cost, BatchUse, Player}|Rest]) ->
    #player_status{id = RoleId} = Player,
    Type = ?NORMAL_TREASURE,
    case check([{times_limit, Type, RoleId}]) of
        true ->
            NormalUseCount = lib_daily:get_count(RoleId, ?MOD_TREASURE_MAP, ?TREASURE_MAP_TYPE_MAX, Type),
            TimesLimit = data_treasure_map:get_kv(map_times_max),
            [{_GoodsType, GoodsId, GoodsNum}|_] = get_treasure_cost(Type),
            %% 每日限制能使用的次数
            AccessCostNum = TimesLimit - NormalUseCount,
            %% 目前拥有的物品数量
            [{GoodsId, MaxNum}|_] = lib_goods_api:get_goods_num(Player, [GoodsId]),
            %% 需要消耗的物品|次数
            % NeedCostNum = GoodsNum*BatchUse,
            NeedCostCount = umath:floor(BatchUse/GoodsNum),
            if 
                BatchUse == 0 ->
                    {false, ?FAIL};
                BatchUse > MaxNum ->
                    {false, ?ERRCODE(goods_not_enough)};
                NeedCostCount =/= 0 andalso NeedCostCount > AccessCostNum ->
                    {false, ?ERRCODE(err424_count_limit)};
                true ->
                    check(Rest)
                % NormalUseCount >= TimesLimit ->
                %     {false, ?ERRCODE(err424_count_limit)};
                % AccessCostNum > MaxNum ->  
                %     if MaxNum =< 0 -> {false, ?ERRCODE(goods_not_enough)};
                %         true -> check(Rest)
                %     end;
                % true ->
                %     if AccessCostNum =< 0 -> {false, ?ERRCODE(err424_count_limit)};
                %         true -> check(Rest)
                %     end
            end;
        {false, Errcode} ->
            {false, Errcode}
    end;
check([_H|Rest]) ->
    check(Rest).

%% 本次shopping成本
calc_shop_goods_cost(Index, BuyNum, OldUseNum, Rewards) ->
    case lists:keyfind(Index, 1, Rewards) of
        {_, {{GoodsType, GoodsId, GoodsNum}, MoneyType, CostNum, LimitBuy}}
         when MoneyType == bgold orelse MoneyType == gold ->
            NewUseNum = BuyNum+OldUseNum,
            if NewUseNum > LimitBuy ->
                    NewBuyNum = LimitBuy - OldUseNum;
                true ->
                    NewBuyNum = BuyNum
            end,
            if BuyNum == 0 orelse NewBuyNum == 0 ->
                    {false, ?FAIL};
                true ->
                    MoneyGoodsType = ?IF(MoneyType == bgold, ?TYPE_BGOLD, ?TYPE_GOLD),
                    CostList = [{MoneyGoodsType, 0, round(CostNum*NewBuyNum)}],
                    RewardsList = [{GoodsType, GoodsId, round(GoodsNum*NewBuyNum)}],
                    {true, CostList, RewardsList, NewBuyNum, LimitBuy}
            end;
        _ ->
            {false, ?FAIL}
    end.

%% 获取随机奖励
%% p.s.: Type - 1普通藏宝图 2高级藏宝图  AddWeight - 增加权重(适用于高级藏宝图)
calc_treasure_rewards(Type, RoleLv, Vip, AddWeight) ->
    RewardIdList  = data_treasure_map:get_reward_list_by_type(Type),
    {FilterList, FilterList1} = lists:foldl(
        fun(RewardId, {TList, TList1}) ->
            case data_treasure_map:get_reward(RewardId) of
                #base_treasure_map_rewards{min_lv = MinLv, max_lv = MaxLv, min_vip = MinVip,
                    get_weight = Weight, show_weight = ShowWeight}
                    when RoleLv >= MinLv andalso RoleLv =< MaxLv andalso Vip >= MinVip ->
                    {[{Weight, RewardId}|TList], [{ShowWeight, RewardId}|TList1]};
                _ -> {TList, TList1}
            end
        end, {[], []}, RewardIdList),
    %% 高级藏宝图有次数权重加成
    IsWeightAdd = case AddWeight > 0 of
                      true -> true;
                      _ -> false
                  end,
    RewardIdList1 =
    case Type of
        ?ADVANCE_TREASURE when IsWeightAdd == true ->
            [
                begin
                    case data_treasure_map:get_reward(TRewardId) of
                        #base_treasure_map_rewards{notice = TNotice} when TNotice > 0 ->
                            {round(TWeight * (1 + AddWeight / 100)) , TRewardId};
                        _ ->
                            {TWeight, TRewardId}
                    end
                end
            ||{TWeight, TRewardId} <- FilterList];
        _ ->
            FilterList
    end,
    RandRewardId  = urand:rand_with_weight(RewardIdList1),
    {GetReward, TvType} = case data_treasure_map:get_reward(RandRewardId) of
                              #base_treasure_map_rewards{notice = Notice} -> {RandRewardId, Notice};
                              _ -> {RandRewardId, 0}
                          end,
    {ShowRewardIdL, _} = ulists:split(10, lists:reverse(lists:keysort(1, FilterList1))),
    ShowRewardIdL1 = [SRewardId||{_SWeight, SRewardId}<-ShowRewardIdL],
    %% 展示列表长度必须维持10
    ShowRewardIdL2 = split_show_rewards(RandRewardId, ShowRewardIdL1),
    {ShowRewardIdL2, GetReward, TvType}.

split_show_rewards(RandRewardId, ShowRewardIdL) ->
    ShowRewardIdL1 = case lists:member(RandRewardId, ShowRewardIdL) of
                         true -> ShowRewardIdL;
                         _ ->
                             case ShowRewardIdL of
                                 [_|_] ->
                                     [_|TShowRewardIdL] = ShowRewardIdL,
                                     [RandRewardId|TShowRewardIdL];
                                 _ ->
                                     [RandRewardId|ShowRewardIdL]
                             end
                     end,
    ShowRewardIdL1.

%% 玩家进程还在时的重连情况 todo:EventId待补充
%% 前端主动请求查询,主动推送忽略
relogin(_Player) -> ok.
%     #player_status{sid = Sid, role_treasure_map = RoleTreasureMap} = Player,
%     RoleTreasureMap1 = trans_treasure_map(RoleTreasureMap),
%     #role_treasure_map{target = Target, type = Type, time = Time, map = DataMap} = RoleTreasureMap1,
%     NowTime = utime:unixtime(),
%     case Target =/= [] andalso Time > NowTime of
%         true ->
%             case Target of
%                 [{SceneId, X, Y}] -> ok;
%                 _ -> SceneId = 0, X = 0, Y = 0
%             end,
%             Event = 
%             case Type of
%                 ?ADVANCE_TREASURE -> 5;
%                 _ -> 
%                     #normal_map{event_id = EventId} = maps:get(?NORMAL_TREASURE, DataMap, #normal_map{}),
%                     EventId
%             end,
%             lib_server_send:send_to_sid(Sid, pt_424, 42401, [?SUCCESS, Type, SceneId, 0, X, Y, Event]),
%             ok;
%         _ ->
%             ignore
%     end.

% trans_treasure_map(RoleTreasureMap) when is_record(RoleTreasureMap, role_treasure_map) ->
%     RoleTreasureMap;
% trans_treasure_map(_) ->
%     #role_treasure_map{}.