hxp
2025-12-31 46dbf29a22bd25d8e9c6ea4c34fea35a4828d712
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerTreasure.py
@@ -17,27 +17,36 @@
import GameWorld
import PlayerRune
import ShareDefine
import GameFuncComm
import IpyGameDataPY
import IPY_GameWorld
import IpyGameDataPY
import FormulaControl
import ChPyNetSendPack
import PlayerControl
import ItemControler
import NetPackCommon
import PlayerBossReborn
import PlayerFeastTravel
import PlayerFairyCeremony
import PlayerNewFairyCeremony
import PlayerActLunhuidian
import PlayerActYunshi
import PlayerActTask
import PlayerActivity
import PlayerSuccess
import PlayerGoldInvest
import OpenServerActivity
import PlayerBillboard
import ShareDefine
import ItemCommon
import PlayerHero
import PyGameData
import PlayerTask
import ChConfig
import random
import time
(
CostType_Money, # 消耗货币 0
CostType_DayFree, # 每日免费 1
CostType_Item, # 消耗道具 2
CostType_ADFree, # 广告免费 3
) = range(4)
CostFreeTypes = [CostType_DayFree, CostType_ADFree]
# 寻宝类型: >=100的为策划自行配置的自定义寻宝类型,<100的用于指定系统寻宝功能
TreasureTypeList = (
@@ -48,14 +57,13 @@
TreasureType_Gubao, # 古宝寻宝 5
) = range(1, 1 + 5)
def DoTreasureOpen(curPlayer):
    ## 寻宝开启
    Sync_TreasureInfo(curPlayer)
    return
TreasureType_HeroComm = 11 # 英雄招募 - 普通
TreasureType_HeroHigh = 12 # 英雄招募 - 高级
TreasureType_HeroScore = 13 # 英雄招募 - 积分
#武将招募的所有类型
TreasureType_HeroCallList = [TreasureType_HeroComm, TreasureType_HeroHigh, TreasureType_HeroScore]
def OnTreasureLogin(curPlayer):
    #if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Treasure):
    #    return
    Sync_TreasureInfo(curPlayer)
    return
@@ -71,6 +79,25 @@
        syncTypeList.append(treasureType)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureCountToday % (treasureType), 0)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureFreeCount % (treasureType), 0)
        # 每日心愿重置
        wishLibSelect = ipyData.GetWishLibSelect()
        wishReset = ipyData.GetWishReset()
        if wishReset == 1 and wishLibSelect:
            for libIDStr in wishLibSelect.keys():
                libID = int(libIDStr)
                libItemList = IpyGameDataPY.GetIpyGameDataList("TreasureItemLib", libID)
                if not libItemList:
                    continue
                for libItem in libItemList:
                    wishID = libItem.GetID()
                    outCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishOut % (treasureType, wishID))
                    if not outCnt:
                        continue
                    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureWishOut % (treasureType, wishID), 0)
                    GameWorld.DebugLog("寻宝每日心愿重置: treasureType=%s,libID=%s,wishID=%s,昨日心愿产出次数=%s" % (treasureType, libID, wishID, outCnt))
                PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureWishLibOut % (treasureType, libID), 0)
    if syncTypeList:
        Sync_TreasureInfo(curPlayer, syncTypeList)
    return
@@ -87,6 +114,7 @@
            ItemControler.RecycleItem(curPlayer, costItemID, recycleItemMail)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureFreeCount % (treasureType), 0)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureCount % (treasureType), 0)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureCountEx % (treasureType), 0)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureCountToday % (treasureType), 0)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureLuck % (treasureType), 0)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureCntAward % (treasureType), 0)
@@ -104,6 +132,108 @@
        return True
    return False
#// A5 69 寻宝心愿物品选择 #tagCSTreasureWishSelect
#
#struct tagCSTreasureWishSelect
#{
#    tagHead        Head;
#    BYTE        TreasureType;    //寻宝类型
#    BYTE        WishCnt;
#    DWORD        WishIDList[WishCnt];    // 选择的寻宝物品库中的数据ID,注意不是库ID
#    BYTE        WishCardUseCnt;
#    WORD        WishCardUseLibIDList[WishCardUseCnt];        // 使用心愿卡的库ID列表
#};
def OnTreasureWishSelect(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    treasureType = clientData.TreasureType
    reqSelectWishIDList = clientData.WishIDList
    wishCardUseLibIDList = clientData.WishCardUseLibIDList
    setIpyData = IpyGameDataPY.GetIpyGameData("TreasureSet", treasureType)
    if not setIpyData:
        return
    wishLibSelect = setIpyData.GetWishLibSelect()
    if not wishLibSelect:
        GameWorld.DebugLog("该寻宝类型没有心愿物品功能! treasureType=%s" % (treasureType))
        return
    GameWorld.DebugLog("寻宝选择心愿物品: treasureType=%s,reqSelectWishIDList=%s" % (treasureType, reqSelectWishIDList))
    selectLibItemDict = {} # 重新选择的心愿物品汇总 {libID:[wishID, ...], ...}
    for wishID in reqSelectWishIDList:
        if not wishID:
            continue
        libItemIpyData = IpyGameDataPY.GetIpyGameDataByCondition("TreasureItemLib", {"ID":wishID}, False)
        if not libItemIpyData:
            return
        itemID = libItemIpyData.GetItemID()
        if not libItemIpyData.GetIsWishItem():
            GameWorld.DebugLog("非心愿物品,不可选择! wishID=%s" % (wishID))
            return
        # 按所属库归类汇总
        libID = libItemIpyData.GetLibID()
        if libID not in selectLibItemDict:
            selectLibItemDict[libID] = []
        selectLibWishIDList = selectLibItemDict[libID]
        if wishID not in selectLibWishIDList:
            selectLibWishIDList.append(wishID)
        # 武将招募,额外限制
        if treasureType in TreasureType_HeroCallList:
            heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", itemID)
            if not heroIpyData:
                return
            if heroIpyData.GetRecruitBySelf() and not PlayerHero.GetHeroActivite(curPlayer, itemID):
                GameWorld.DebugLog("需要激活本体的武将未激活不可选择!itemID=%s" % itemID)
                return
    # 公共次数模式,不限制切换
    if setIpyData.GetWishLibPubFreeCnt():
        # 无限制,直接保存
        pass
    # 独立次数模式,有产出后的心愿物品无法切换
    else:
        GameWorld.DebugLog("重选心愿库对应ID汇总: %s" % selectLibItemDict)
        for libIDStr, wishCnt in wishLibSelect.items():
            libID = int(libIDStr)
            selectLibWishIDList = selectLibItemDict.get(libID, [])
            if selectLibWishIDList and len(selectLibWishIDList) != wishCnt:
                GameWorld.DebugLog("选择心愿库的物品数量与设定的心愿物品数量不一致!libID=%s,wishCnt=%s,selectCnt=%s,%s"
                                   % (libID, wishCnt, len(selectLibWishIDList), selectLibWishIDList))
                return
            libItemList = IpyGameDataPY.GetIpyGameDataList("TreasureItemLib", libID)
            if not libItemList:
                return
            for libItem in libItemList:
                wishID = libItem.GetID()
                outCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishOut % (treasureType, wishID))
                if not outCnt:
                    continue
                if wishID not in selectLibWishIDList:
                    GameWorld.DebugLogEx("已经产出过的心愿物品不可从选择中去除! outCnt=%s,wishID=%s not in %s", outCnt, wishID, selectLibWishIDList)
                    return
    # 验证通过,保存
    for libIDStr, wishCnt in wishLibSelect.items():
        libID = int(libIDStr)
        wishIDList = selectLibItemDict.get(libID, [])
        for wishIndex in range(wishCnt):
            wishID = wishIDList[wishIndex] if len(wishIDList) > wishIndex else 0
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureWishSelect % (treasureType, libIDStr, wishIndex), wishID)
            GameWorld.DebugLog("保存心愿选择: libID=%s,wishIndex=%s,wishID=%s" % (libID, wishIndex, wishID))
        isUse = 1 if libID in wishCardUseLibIDList else 0
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureWishUseItem % (treasureType, libID), isUse)
        GameWorld.DebugLog("保存心愿卡是否使用: libID=%s,isUse=%s" % (libID, isUse))
    Sync_TreasureInfo(curPlayer, [treasureType])
    return
#// A5 68 请求寻宝 #tagCMRequestTreasure
#
#struct tagCMRequestTreasure
@@ -115,12 +245,15 @@
#};
def OnRequestTreasure(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    playerLV = curPlayer.GetLV()
    playerID = curPlayer.GetPlayerID()
    treasureType = clientData.TreasureType
    treasureIndex = clientData.TreasureIndex
    costType = clientData.CostType
    DoTreasure(curPlayer, treasureType, costType, treasureIndex)
    return
def DoTreasure(curPlayer, treasureType, costType, treasureIndex=0):
    playerLV = curPlayer.GetLV()
    playerID = curPlayer.GetPlayerID()
    GameWorld.DebugLog("玩家寻宝: treasureType=%s,treasureIndex=%s,costType=%s,playerLV=%s" 
                       % (treasureType, treasureIndex, costType, playerLV), playerID)
    
@@ -135,6 +268,9 @@
    if not treasureCountList:
        GameWorld.DebugLog("没有寻宝次数列表配置!", playerID)
        return
    if costType == CostType_ADFree:
        treasureIndex = 0
        GameWorld.DebugLog("广告寻宝强制设置: treasureIndex=%s" % treasureIndex, playerID)
    if treasureIndex < 0 or treasureIndex >= len(treasureCountList):
        GameWorld.ErrLog("寻宝次数索引不存在!treasureType=%s,treasureIndex=%s" % (treasureType, treasureIndex), playerID)
        return
@@ -157,7 +293,7 @@
            return
        
    # 免费次数
    if costType == 1:
    if costType == CostType_DayFree:
        dailyFreeCount = setIpyData.GetDailyFreeCount()
        if not dailyFreeCount:
            GameWorld.ErrLog("该寻宝类型索引不支持免费次数寻宝!treasureType=%s,treasureIndex=%s" % (treasureType, treasureIndex), playerID)
@@ -167,9 +303,11 @@
        if updFreeCountToday > dailyFreeCount:
            GameWorld.DebugLog("今日免费次数不足,无法使用免费寻宝! freeCountToday=%s + %s > %s" % (freeCountToday, treasureCount, dailyFreeCount), playerID)
            return
    # 广告免费
    elif costType == CostType_ADFree:
        pass
    # 寻宝道具, 目前默认消耗1个
    elif costType == 2:
    elif costType == CostType_Item:
        costItemID = setIpyData.GetCostItemID()
        costItemList = setIpyData.GetCostItemCountList() # 消耗道具物品ID列表
        if not costItemID or not costItemList or treasureIndex >= len(costItemList):
@@ -222,20 +360,20 @@
        GameWorld.ErrLog("找不到该等级对应寻宝库配置!treasureType=%s,curLV=%s" % (treasureType, curPlayer.GetLV()), playerID)
        return
    
    luckyItemRateList = ipyData.GetLuckyItemRateList()
    luckyGridNumList = []
    if luckyItemRateList:
        for _, gridNum in luckyItemRateList:
            luckyGridNumList.append(gridNum)
    elif setIpyData.GetLuckyGridNum():
        luckyGridNumList = [setIpyData.GetLuckyGridNum()]
    GameWorld.DebugLog("luckyGridNumList=%s, %s" % (luckyGridNumList, luckyItemRateList), playerID)
    setLuckyGridNum = setIpyData.GetLuckyGridNum() # 标的格子
    luckyItemRateInfo = ipyData.GetLuckyItemRateInfo()
    luckyItemRateDict = {int(k):v for k, v in luckyItemRateInfo.items()}
    luckyValueList = sorted(luckyItemRateDict.keys())
    luckyGridNumList = [] # 幸运格子编号列表
    luckFormula = setIpyData.GetLuckyRateFormat() # 幸运物品概率公式
    addLuck = setIpyData.GetOnceLucky() # 增加幸运值
    maxLuck = setIpyData.GetFullLucky() # 满幸运值
    curLuck = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureLuck % (treasureType)) # 当前幸运值
    updLuck = curLuck
    maxLuck = max(luckyValueList) if luckyValueList else 0 # 满幸运值
    updLuck = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureLuck % (treasureType)) # 当前幸运值
    GameWorld.DebugLog("updLuck=%s,maxLuck=%s,setLuckyGridNum=%s,luckyItemRateDict=%s" % (updLuck, maxLuck, setLuckyGridNum, luckyItemRateDict), playerID)
    if treasureType in TreasureType_HeroCallList and not PlayerGoldInvest.GetInvestState(curPlayer, ChConfig.InvestType_Life):
        addLuck = 0
        GameWorld.DebugLog("终身卡未开通,武将招募不增加幸运", playerID)
    curTreasureCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureCount % (treasureType)) # 当前已寻宝次数
    updTreasureCount = curTreasureCount
    
@@ -245,69 +383,176 @@
    GameWorld.DebugLog("beSureCountDict=%s" % beSureCountDict, playerID)
    GameWorld.DebugLog("ensureCount=%s, %s" % (ensureCount, ensureRateList), playerID)
    notifyGridNumList = setIpyData.GetNotifyGridNumList() # 额外需要广播的格子,幸运必出、次数必出可不配置
    notifyKey = setIpyData.GetNotifyKey()
    notifyKeyDict = setIpyData.GetNotifyKeyDict()
    gridNumMaxLimitInfo = setIpyData.GetGridNumMaxLimitInfo() # {"格子":最大可产出次数, ...}
    gridNumCountInfo = {} # 有限制产出次数的格子已经产出数
    for gridNumStr in gridNumMaxLimitInfo.keys():
        gridNumCountInfo[int(gridNumStr)] = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureGridCnt % (treasureType, gridNumStr))
    GameWorld.DebugLog("gridNumMaxLimitInfo=%s,gridNumCountInfo=%s" % (gridNumMaxLimitInfo, gridNumCountInfo), playerID)
    
    treasureCountEx = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureCountEx % (treasureType)) # 当前第x次单抽、x抽
    curIndexCount, maxIndexCount = 0, 0
    beSureCountByIndexDict = {}
    beSureCountByIndexList = ipyData.GetGridItemRateList4() # 第x次x抽必出,最多支持定制到9次
    if beSureCountByIndexList and treasureIndex < len(beSureCountByIndexList):
        beSureCountByIndexDict = beSureCountByIndexList[treasureIndex]
        maxIndexCount = min(9, max(beSureCountByIndexDict))
        curIndexCount = GameWorld.GetDataByDigitPlace(treasureCountEx, treasureIndex) + 1
    beSureCountByIndexCfg = []
    if curIndexCount <= maxIndexCount and curIndexCount in beSureCountByIndexDict:
        beSureCountByIndexCfg = beSureCountByIndexDict[curIndexCount]
    gridItemInfoDict = ipyData.GetGridItemInfo() # 格子对应物品信息 {"格子编号":[物品ID, 数量], ...}
    gridLibInfoDict = ipyData.GetGridLibInfo() # 格子编号对应库ID {"编号":物品库ID, ...}
    # 心愿设定
    wishLibSelect = setIpyData.GetWishLibSelect() # {"心愿库":可选择物品数, ...}
    wishPubFreeCntDict = setIpyData.GetWishLibPubFreeCnt() # 心愿库公共免费次数 {"心愿库":免费次数, ...}
    wishPubCardDict = setIpyData.GetWishLibCard() # 心愿库公共次数心愿卡 {"心愿库":心愿卡ID, ...}
    # 心愿优先产出相关
    preOutWishDict = {} # 心愿物品预计产出数{libID:预计优先产出数, ...}
    retOutWishDict = {} # 心愿物品实际产出数{libID:{wishID:实际优先产出数, ...}, ...}
    selectWishIDDict = {} # 库当前对应选择的心愿物品 {libID:[wishID, ...], ...}
    # 公共心愿
    canFreeOutWishLibDict = {} # 心愿物品库公共还可免费产出数 {libID:还可免费产出数, ...}
    wishCardItemLibDict = {} # 公共心愿次数心愿卡背包物品 {libID:wishCardItem, ...}
    # 独立心愿
    canFreeOutWishIDict = {} # 心愿物品独立还可免费产出数 {wishID:还可免费产出数, ...}
    for libIDStr, selectCnt in wishLibSelect.items():
        libID = int(libIDStr)
        if wishPubFreeCntDict:
            outTotal = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishLibOut % (treasureType, libID))
            freeCnt = wishPubFreeCntDict.get(libIDStr, 0)
            canFreeOutPub = freeCnt - outTotal
            if canFreeOutPub > 0:
                canFreeOutWishLibDict[libID] = canFreeOutPub
        for wishIndex in range(selectCnt):
            wishID = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishSelect % (treasureType, libIDStr, wishIndex))
            if not wishID:
                continue
            libItemIpyData = IpyGameDataPY.GetIpyGameDataByCondition("TreasureItemLib", {"ID":wishID}, False)
            if not libItemIpyData:
                continue
            if not libItemIpyData.GetIsWishItem():
                # 非心愿物品
                continue
            if libID not in selectWishIDDict:
                selectWishIDDict[libID] = []
            selectWishIDList = selectWishIDDict[libID]
            selectWishIDList.append(wishID)
            # 公共次数
            if wishPubFreeCntDict:
                pass
            # 独立次数
            else:
                outCntLimit = libItemIpyData.GetWishOutCnt()
                outCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishOut % (treasureType, wishID))
                canFreeOut = outCntLimit - outCnt
                if canFreeOut <= 0:
                    continue
                canFreeOutWishIDict[wishID] = canFreeOut
    if wishLibSelect:
        GameWorld.DebugLog("当前心愿库选择的心愿ID列表: %s" % selectWishIDDict, playerID)
        GameWorld.DebugLog("还可优先产出的心愿免费次数:%s" % canFreeOutWishLibDict, playerID)
    # 单抽产出优先级: 幸运物品 > 必出 > 保底 > 普通
    # 连抽没有优先级限制,只要满足条件即可产出
    getGridResult = []
    for tIndex in range(treasureCount):
        updLuck = min(updLuck + addLuck, maxLuck)
        stageLuck, luckItemRateList = __getLuckyRateInfo(updLuck, luckyItemRateDict, luckyValueList)
        luckyGridNumList = []
        for _, gridNum in luckItemRateList:
            luckyGridNumList.append(gridNum)
        updTreasureCount += 1
        GameWorld.DebugLog("%s,累计次数=%s,幸运=%s" % (tIndex + 1, updTreasureCount, updLuck), playerID)
        GameWorld.DebugLog("%s,累计次数=%s,幸运=%s,阶段幸运=%s,幸运饼图=%s" % (tIndex + 1, updTreasureCount, updLuck, stageLuck, luckItemRateList), playerID)
        if gridNumMaxLimitInfo:
            GameWorld.DebugLog("    gridNumMaxLimitInfo=%s,gridNumCountInfo=%s" % (gridNumMaxLimitInfo, gridNumCountInfo), playerID)
        baseRateList, commItemRateList = GetUpdLuckyItemRateList(ipyData, luckyGridNumList, updLuck, luckFormula, costType) # 常规产出物品格子饼图,幸运物品概率已变更
        commItemRateList = GetRemoveLimitGridRateList(commItemRateList, gridNumCountInfo, gridNumMaxLimitInfo)        
        GameWorld.DebugLog("    基础饼图=%s" % baseRateList, playerID)
        GameWorld.DebugLog("    常规饼图=%s" % commItemRateList, playerID)
        GameWorld.DebugLog("    基础产出饼图=%s" % baseRateList, playerID)
        
        curRateList = [] # 可能会改变饼图,每次抽奖使用新的饼图对象,不要改变配置的饼图概率
        
        # 满幸运必出
        if updLuck >= maxLuck and luckyGridNumList:
            if luckyItemRateList:
                curRateList = GetRemoveLimitGridRateList(luckyItemRateList, gridNumCountInfo, gridNumMaxLimitInfo)
        # 第x次x抽必出,优先级最高,无视其他
        if not curRateList and beSureCountByIndexCfg:
            if tIndex == 0:
                curRateList = [[10000, beSureCountByIndexCfg[0]]]
            else:
                curRateList = GetRemoveLimitGridRateList([(10000, luckyGridNumList[0])], gridNumCountInfo, gridNumMaxLimitInfo)
            GameWorld.DebugLog("    【满幸运饼图】: %s" % curRateList)
                curRateList = beSureCountByIndexCfg[1]
            GameWorld.DebugLog("    【第x次x抽必出】: treasureIndex=%s,curIndexCount=%s,%s"
                               % (treasureIndex, curIndexCount, curRateList), playerID)
        # 满幸运必出
        if not curRateList and stageLuck and updLuck >= stageLuck and luckItemRateList:
            curRateList = GetRemoveLimitGridRateList(luckItemRateList, gridNumCountInfo, gridNumMaxLimitInfo)
            GameWorld.DebugLog("    【满幸运必出饼图】: %s" % curRateList, playerID)
            
        # 次数必出
        if not curRateList and updTreasureCount in beSureCountDict:
            besureGridRateList = beSureCountDict[updTreasureCount]
            curRateList = GetRemoveLimitGridRateList(besureGridRateList, gridNumCountInfo, gridNumMaxLimitInfo)
            GameWorld.DebugLog("    【第%s次数必出饼图】: %s" % (updTreasureCount, curRateList))
            GameWorld.DebugLog("    【第%s次数必出饼图】: %s" % (updTreasureCount, curRateList), playerID)
            
        # 满次数必出
        if not curRateList and ensureCount and updTreasureCount % ensureCount == 0 and ensureRateList:
            curRateList = GetRemoveLimitGridRateList(ensureRateList, gridNumCountInfo, gridNumMaxLimitInfo)
            GameWorld.DebugLog("    【满%s次数必出饼图】: %s" % (ensureCount, curRateList))
            GameWorld.DebugLog("    【满%s次数必出饼图】: %s" % (ensureCount, curRateList), playerID)
            
        doCount = 0
        while doCount <= 50: # 限制最大次数
            doCount += 1
            if doCount > 1 or not curRateList: # 重新随机的默认使用常规饼图
                curRateList = commItemRateList
                GameWorld.DebugLog("    使用常规饼图=%s" % curRateList, playerID)
                
            gridNum = GameWorld.GetResultByRandomList(curRateList)
            if gridNum in luckyGridNumList and gridNum in getGridResult:
                GameWorld.DebugLog("    幸运物品已经出过,不再重复产出! gridNum=%s in %s" % (gridNum, getGridResult))
                GameWorld.DebugLog("    幸运物品已经出过,不再重复产出重新随机! gridNum=%s in %s" % (gridNum, getGridResult), playerID)
                continue
            
            # 其他产出限制...
            # 心愿库物品,检查心愿预产出
            gridNumStr = str(gridNum)
            wishLibID = 0
            if wishLibSelect and gridNumStr in gridLibInfoDict and str(gridLibInfoDict[gridNumStr]) in wishLibSelect:
                wishLibID = gridLibInfoDict[gridNumStr]
                if wishPubFreeCntDict:
                    # 公共心愿默认均可正常产出,只是处理是否优先产出心愿
                    __prePubWishOut(curPlayer, treasureType, gridNum, wishLibID, selectWishIDDict,
                                    preOutWishDict, canFreeOutWishLibDict, wishPubCardDict, wishCardItemLibDict)
                #else:
                #    # 非公共的暂不支持,后续有需要再处理
                #    return
            if not gridNum:
                continue
            
            getGridResult.append(gridNum)
            GameWorld.DebugLog("    本次产出: gridNum=%s, %s" % (gridNum, getGridResult), playerID)
            if gridNum in luckyGridNumList:
                updLuck = 0
                GameWorld.DebugLog("    【产出幸运格子】: gridNum=%s" % (gridNum), playerID)
            GameWorld.DebugLog("    本次产出: gridNum=%s, %s, doCount=%s" % (gridNum, getGridResult, doCount), playerID)
            if gridNum in luckyGridNumList or updLuck >= maxLuck:
                if addLuck:
                    if gridNum == setLuckyGridNum or updLuck >= maxLuck:
                        updLuck = 0
                    else:
                        updLuck = stageLuck # 直接切换到下一阶段幸运
                else:
                    updLuck = 0
                    GameWorld.DebugLog("    不加幸运时强制重置幸运值: gridNum=%s,updLuck=%s" % (gridNum, updLuck), playerID)
                GameWorld.DebugLog("    【产出幸运格子】: gridNum=%s,updLuck=%s" % (gridNum, updLuck), playerID)
            if wishLibID:
                GameWorld.DebugLog("    【产出的是心愿库物品】: gridNum=%s,wishLibID=%s" % (gridNum, wishLibID), playerID)
            if gridNum in gridNumCountInfo:
                gridNumCountInfo[gridNum] = gridNumCountInfo[gridNum] + 1
                GameWorld.DebugLog("    【更新产出次数】: gridNum=%s, %s" % (gridNum, gridNumCountInfo), playerID)
@@ -320,8 +565,6 @@
    
    isBind = 0 # 暂时默认不绑定
    job = curPlayer.GetJob()
    gridItemInfoDict = ipyData.GetGridItemInfo() # 格子对应物品信息 {"格子编号":[物品ID, 数量], ...}
    gridLibInfoDict = ipyData.GetGridLibInfo() # 格子编号对应库ID {"编号":物品库ID, ...}
    jobItemList = ipyData.GetJobItemList()
    treasureResult = []
    randItemIDDict = IpyGameDataPY.GetFuncEvalCfg("TreasureSet", 2)
@@ -340,9 +583,7 @@
                canRandItemList = []
                randItemIDList = randItemIDDict[itemID]
                for randItemID in randItemIDList:
                    itemData = GameWorld.GetGameData().GetItemByTypeID(randItemID)
                    if itemData.GetType() == ChConfig.Def_ItemType_Rune and not PlayerRune.GetIsOpenByRuneID(curPlayer, randItemID):
                        GameWorld.DebugLog("未解锁的符印不产出!itemID=%s,randItemID=%s" % (itemID, randItemID), playerID)
                    if not __checkItemCanTreasure(curPlayer, treasureType, randItemID):
                        continue
                    canRandItemList.append(randItemID)
                if not canRandItemList:
@@ -356,29 +597,58 @@
            libItemList = IpyGameDataPY.GetIpyGameDataList("TreasureItemLib", libID)
            if not libItemList:
                return
            wishWeightList = [] # 心愿物品权重
            itemWeightList = []
            for libItem in libItemList:
                curID = libItem.GetID()
                itemWeight, itemID, itemCount = libItem.GetItemWeight(), libItem.GetItemID(), libItem.GetItemCount()
                itemData = GameWorld.GetGameData().GetItemByTypeID(itemID)
                if not itemData:
                if not itemWeight:
                    continue
                if not __checkItemCanTreasure(curPlayer, treasureType, itemID):
                    continue
                itemWeightList.append([itemWeight, [itemID, itemCount]])
                # 公共次数
                if wishPubFreeCntDict:
                    selectWishIDList = selectWishIDDict.get(libID, [])
                    if curID not in selectWishIDList:
                        continue
                    preOutWishCnt = preOutWishDict.get(libID, 0)
                    if preOutWishCnt <= 0:
                        continue
                    preOutWishDict[libID] = preOutWishCnt - 1
                    wishWeightList.append([itemWeight, [itemID, itemCount, curID]])
            if not itemWeightList:
                GameWorld.ErrLog("寻宝随机格子没有可随机的物品!treasureType=%s,treasureIndex=%s,gridNum=%s,libID=%s" 
                                 % (treasureType, treasureIndex, gridNum, libID), playerID)
                return
            itemID, itemCount = GameWorld.GetResultByWeightList(itemWeightList)
            # 优先产出选择的心愿物品
            if wishWeightList:
                itemID, itemCount, curID = GameWorld.GetResultByWeightList(wishWeightList)
                GameWorld.DebugLog("优先产出心愿物品: gridNum=%s,libID=%s,wishID=%s,itemID=%s" % (gridNum, libID, curID, itemID), playerID)
                if libID not in retOutWishDict:
                    retOutWishDict[libID] = {}
                retOutWishIDDict = retOutWishDict[libID]
                retOutWishIDDict[curID] = retOutWishIDDict.get(curID, 0) + 1 # 累加实际优先产出
            else:
                itemID, itemCount = GameWorld.GetResultByWeightList(itemWeightList)
        else:
            GameWorld.ErrLog("寻宝格子不存在!treasureType=%s,gridNum=%s" % (treasureType, gridNum), playerID)
            return
        
        treasureResult.append([gridNum, itemID, itemCount, isBind])
        isTrans = 0 # 是否转化
        treasureResult.append([gridNum, itemID, itemCount, isTrans])
        
    # 扣消耗
    if costType == 1:
    if costType == CostType_DayFree:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureFreeCount % (treasureType), updFreeCountToday)
        GameWorld.DebugLog("消耗免费次数,更新今日已使用免费次数: %s" % updFreeCountToday, playerID)
    elif costType == 2:
    elif costType == CostType_ADFree:
        GameWorld.DebugLog("广告寻宝免费", playerID)
    elif costType == CostType_Item:
        ItemCommon.DelCostItemByBind(curPlayer, costItemIndexList, bindCnt, unBindCnt, delCostItemCount, ChConfig.ItemDel_Treasure)
        GameWorld.DebugLog("扣除寻宝道具,costItemID=%s,delCostItemCount=%s" % (costItemID, delCostItemCount), playerID)
        if lackCountCostMoney:
@@ -392,60 +662,89 @@
    # 加数据
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureCountToday % (treasureType), updTreasureCountToday)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureCount % (treasureType), updTreasureCount)
    for luckyGridNum in luckyGridNumList:
        if luckyGridNum in getGridResult:
            updLuck = 0
            break
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureLuck % (treasureType), updLuck)
    for gridNum, updCount in gridNumCountInfo.items():
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureGridCnt % (treasureType, gridNum), updCount)
    if curIndexCount <= maxIndexCount:
        treasureCountEx = GameWorld.ChangeDataByDigitPlace(treasureCountEx, treasureIndex, curIndexCount)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureCountEx % (treasureType), treasureCountEx)
        GameWorld.DebugLog("更新第x次x抽次数: treasureIndex=%s,curIndexCount=%s,maxIndexCount=%s,treasureCountEx=%s" % (treasureIndex, curIndexCount, maxIndexCount, treasureCountEx), playerID)
    # 心愿产出次数
    for libID, retOutWishIDDict in retOutWishDict.items():
        retOutTotal = 0
        for wishID, retOutCnt in retOutWishIDDict.items():
            retOutTotal += retOutCnt
            outCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishOut % (treasureType, wishID))
            updOutCnt = outCnt + retOutCnt
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureWishOut % (treasureType, wishID), updOutCnt)
            GameWorld.DebugLog("更新心愿物品优先产出次数: libID=%s,wishID=%s,retOutCnt=%s,updOutCnt=%s" % (libID, wishID, retOutCnt, updOutCnt), playerID)
        outTotal = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishLibOut % (treasureType, libID))
        updOutTotal = outTotal + retOutTotal
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_TreasureWishLibOut % (treasureType, libID), updOutTotal)
        GameWorld.DebugLog("更新心愿库物品累计优先产出次数: libID=%s,retOutTotal=%s,updOutTotal=%s" % (libID, retOutTotal, updOutTotal), playerID)
        
        canFreeCnt = canFreeOutWishLibDict.get(libID, 0)
        costWishCardCnt = retOutTotal - canFreeCnt
        if costWishCardCnt > 0 and libID in wishCardItemLibDict:
            wishCardItem = wishCardItemLibDict[libID]
            cardItemID = wishCardItem.GetItemTypeID() if wishCardItem else 0
            costWishCardCnt = min(costWishCardCnt, ItemControler.GetItemCount(wishCardItem))
            GameWorld.DebugLog("扣除心愿卡个数: cardItemID=%s,costWishCardCnt=%s" % (cardItemID, costWishCardCnt), playerID)
            if wishCardItem:
                ItemCommon.DelItem(curPlayer, wishCardItem, costWishCardCnt)
    addScoreType = setIpyData.GetAwardMoneyType() # 额外奖励货币类型
    addScore = setIpyData.GetAwardMoneyValue() # 单次奖励货币数
    if addScoreType and addScore:
        PlayerControl.GiveMoney(curPlayer, addScoreType, addScore)
        PlayerControl.GiveMoney(curPlayer, addScoreType, addScore * treasureCount)
        
    if treasureType == TreasureType_Rune:
        PlayerFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_RuneTreasure, treasureCount)
        PlayerFeastTravel.AddFeastTravelTaskValue(curPlayer, ChConfig.Def_FeastTravel_RuneTreasure, treasureCount)
        PlayerBossReborn.AddBossRebornActionCnt(curPlayer, ChConfig.Def_BRAct_RuneTreasure, treasureCount)
        PlayerActTask.AddActTaskValue(curPlayer, ChConfig.ActTaskType_TreasureRune, treasureCount)
    elif treasureType == TreasureType_Jipin:
        PlayerFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_Treasure, treasureCount)
        PlayerFeastTravel.AddFeastTravelTaskValue(curPlayer, ChConfig.Def_FeastTravel_Treasure, treasureCount)
        PlayerBossReborn.AddBossRebornActionCnt(curPlayer, ChConfig.Def_BRAct_Treasure, treasureCount)
        PlayerActTask.AddActTaskValue(curPlayer, ChConfig.ActTaskType_TreasureJipin, treasureCount)
    elif treasureType == TreasureType_Jueshi:
        PlayerFairyCeremony.AddFCPartyActionCnt(curPlayer, ChConfig.Def_PPAct_JSTreasure, treasureCount)
        PlayerFeastTravel.AddFeastTravelTaskValue(curPlayer, ChConfig.Def_FeastTravel_JSTreasure, treasureCount)
        PlayerBossReborn.AddBossRebornActionCnt(curPlayer, ChConfig.Def_BRAct_JSTreasure, treasureCount)
        PlayerActTask.AddActTaskValue(curPlayer, ChConfig.ActTaskType_TreasureJueshi, treasureCount)
    elif treasureType == TreasureType_GatherTheSoul:
        PlayerActTask.AddActTaskValue(curPlayer, ChConfig.ActTaskType_TreasureGatherTheSoul, treasureCount)
    elif treasureType == TreasureType_Gubao:
        PlayerActTask.AddActTaskValue(curPlayer, ChConfig.ActTaskType_TreasureGubao, treasureCount)
    if treasureType in TreasureType_HeroCallList:
        PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_HeroCall, treasureCount)
        PlayerActivity.AddDailyTaskValue(curPlayer, ChConfig.DailyTask_HeroCall, treasureCount)
        heroCallCnt = GetHeroCallCnt(curPlayer)
        if OpenServerActivity.GetOSAState(curPlayer, ShareDefine.Def_BT_OSA_HeroCall) == 1:
            PlayerBillboard.UpdatePlayerBillboard(curPlayer, ShareDefine.Def_BT_OSA_HeroCall, heroCallCnt)
        PlayerSuccess.UptateSuccessProgress(curPlayer, ShareDefine.SuccType_OSAHeroCall, heroCallCnt)
        
    PlayerActLunhuidian.AddLunhuidianValue(curPlayer, PlayerActLunhuidian.AwardType_Treasure, treasureType, treasureCount)
    
    # 给物品
    mailItemList = []
    itemControl = ItemControler.PlayerItemControler(curPlayer)
    for gridNum, itemID, itemCount, isBind in treasureResult:
        itemObj = ItemControler.GetOutPutItemObj(itemID, itemCount, False, curPlayer=curPlayer)
    for tResult in treasureResult:
        gridNum, itemID, itemCount = tResult[:3]
        PyGameData.g_transItemSign = 0
        itemObj = ItemControler.GetOutPutItemObj(itemID, itemCount, isBind, curPlayer=curPlayer)
        mailItemDict = ItemCommon.GetMailItemDict(itemObj)
        
        if int(gridNum) in notifyGridNumList and notifyKey:
            PlayerControl.WorldNotify(0, notifyKey, [curPlayer.GetPlayerName(), itemID, itemObj.GetUserData(), itemCount])
        if int(gridNum) in notifyGridNumList and notifyKeyDict:
            notifyKey = notifyKeyDict.get(int(gridNum), notifyKeyDict.get(0, ""))
            if treasureType in TreasureType_HeroCallList:
                if PlayerHero.GetHeroActivite(curPlayer, itemID):
                    notifyKey = ""
                    GameWorld.DebugLog("招募武将非首次获得的不广播了! itemID=%s" % itemID, playerID)
                elif notifyKey:
                    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", itemID)
                    if heroIpyData:
                        heroQuality = heroIpyData.GetQuality()
                        PlayerControl.WorldNotify(0, notifyKey, [curPlayer.GetPlayerName(), heroQuality, itemID])
            elif notifyKey:
                PlayerControl.WorldNotify(0, notifyKey, [curPlayer.GetPlayerName(), itemID, itemObj.GetUserData(), itemCount])
            
        if mailItemList or not itemControl.PutInItem(packType, itemObj, event=[ChConfig.ItemGive_Treasure, False, {}]):
            mailItemList.append(mailItemDict)
            itemObj.Clear()
            
        # 检查物品转化
        if PyGameData.g_transItemSign:
            tResult[3] = 1 # 有转化物品时设置转化标记
    if mailItemList:
        PlayerControl.SendMailByKey("HappyXBUnEnough", [playerID], mailItemList)
        
    GameWorld.DebugLog("寻宝成功: treasureType=%s,updTreasureCount=%s(%s),updLuck=%s,addScoreType=%s,addScore=%s,gridNumCountInfo=%s"
                       % (treasureType, updTreasureCount, updTreasureCountToday, updLuck, addScoreType, addScore, gridNumCountInfo), playerID)
    GameWorld.DebugLog("寻宝成功: treasureType=%s,updTreasureCount=%s(%s),updLuck=%s,addScoreType=%s,addScore=%s,gridNumCountInfo=%s,treasureCountEx=%s"
                       % (treasureType, updTreasureCount, updTreasureCountToday, updLuck, addScoreType, addScore, gridNumCountInfo, treasureCountEx), playerID)
    GameWorld.DebugLog("    treasureResult=%s" % (treasureResult), playerID)
    GameWorld.DebugLog("    mailItemList=%s" % (mailItemList), playerID)
    
@@ -461,6 +760,91 @@
    
    Sync_TreasureInfo(curPlayer, [treasureType])
    return
def __prePubWishOut(curPlayer, treasureType, gridNum, wishLibID, selectWishIDDict, preOutWishDict,
                    canFreeOutWishLibDict, wishPubCardDict, wishCardItemLibDict):
    ## 公共心愿产出预处理
    playerID = curPlayer.GetPlayerID()
    selectWishIDList = selectWishIDDict.get(wishLibID, [])
    if not selectWishIDList:
        GameWorld.DebugLog("    公共心愿未选择心愿物品,走默认随机规则", playerID)
        return
    preOutTotal = preOutWishDict.get(wishLibID, 0)
    canFreeCnt = canFreeOutWishLibDict.get(wishLibID, 0)
    GameWorld.DebugLog("    公共免费心愿次数! gridNum=%s,wishLibID=%s,preOutTotal=%s,canFreeCnt=%s"
                       % (gridNum, wishLibID, preOutTotal, canFreeCnt), playerID)
    if preOutTotal >= canFreeCnt:
        if not curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishUseItem % (treasureType, wishLibID)):
            GameWorld.DebugLog("    玩家心愿卡未启用,走默认随机规则! gridNum=%s,wishLibID=%s" % (gridNum, wishLibID), playerID)
            return
        wishCardID = wishPubCardDict.get(str(wishLibID), 0)
        if not wishCardID:
            GameWorld.DebugLog("    该库没有心愿卡配置,走默认随机规则! gridNum=%s,wishLibID=%s" % (gridNum, wishLibID), playerID)
            return
        if wishLibID not in wishCardItemLibDict:
            wishCardItem = ItemCommon.FindItemInPackByItemID(curPlayer, wishCardID, IPY_GameWorld.rptItem)
            if not wishCardItem:
                GameWorld.DebugLog("    玩家没有对应心愿卡物品,走默认随机规则! gridNum=%s,wishLibID=%s,wishCardID=%s"
                                   % (gridNum, wishLibID, wishCardID), playerID)
                return
            wishCardItemLibDict[wishLibID] = wishCardItem
        wishCardItem = wishCardItemLibDict[wishLibID]
        cardItemCount = ItemControler.GetItemCount(wishCardItem)
        canOutTotal = canFreeCnt + cardItemCount
        if preOutTotal >= canOutTotal:
            GameWorld.DebugLog("    心愿卡个数预产出已消耗完,走默认随机规则! gridNum=%s,wishLibID=%s,wishCardID=%s"
                               % (gridNum, wishLibID, wishCardID), playerID)
            return
        GameWorld.DebugLog("    心愿卡个数还可产出! gridNum=%s,wishLibID=%s,wishCardID=%s,cardItemCount=%s"
                                   % (gridNum, wishLibID, wishCardID, cardItemCount), playerID)
    else:
        GameWorld.DebugLog("        公共心愿还有免费次数强制消耗次数", playerID)
    preOutWishDict[wishLibID] = preOutTotal + 1
    return
def GetHeroCallCnt(curPlayer):
    ## 获取武将招募总次数
    callCount = 0
    for treasureType in TreasureType_HeroCallList:
        callCount += curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureCount % (treasureType))
    return callCount
def __getLuckyRateInfo(curLuck, luckyItemRateDict, luckyValueList):
    if not luckyItemRateDict or not luckyValueList:
        return 0, []
    for luck in luckyValueList:
        if curLuck <= luck:
            return luck, luckyItemRateDict[luck]
    lastLuck = luckyValueList[-1]
    return lastLuck, luckyItemRateDict[lastLuck]
def __checkItemCanTreasure(curPlayer, treasureType, itemID):
    ## 检查物品ID是否可寻宝产出
    itemData = GameWorld.GetGameData().GetItemByTypeID(itemID)
    if not itemData:
        return
    playerID = curPlayer.GetPlayerID()
    if itemData.GetType() == ChConfig.Def_ItemType_Hero:
        heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", itemID)
        if not heroIpyData:
            return
        if heroIpyData.GetRecruitBySelf() and not PlayerHero.GetHeroActivite(curPlayer, itemID):
            GameWorld.DebugLog("武将未激活不产出! itemID=%s" % itemID, playerID)
            return
    elif itemData.GetType() == ChConfig.Def_ItemType_Rune:
        if not PlayerRune.GetIsOpenByRuneID(curPlayer, itemID):
            GameWorld.DebugLog("未解锁的符印不产出! itemID=%s" % itemID, playerID)
            return
    return True
def GetRemoveLimitGridRateList(srcGridNumRateList, gridNumCountInfo, gridNumMaxLimitInfo):
    ## 获取移除限制产出的格子后的饼图列表
@@ -485,7 +869,7 @@
def GetUpdLuckyItemRateList(ipyData, luckyGridNumList, curLuck, luckFormula, costType):
    # 获取幸运物品提升概率后的饼图
    treasureType = ipyData.GetTreasureType()
    srcPieList = ipyData.GetGridItemRateListFree() if costType == 1 else ipyData.GetGridItemRateList1()
    srcPieList = ipyData.GetGridItemRateListFree() if costType in CostFreeTypes else ipyData.GetGridItemRateList1()
    if not srcPieList:
        srcPieList = ipyData.GetGridItemRateList1()
        
@@ -582,6 +966,27 @@
            gridLimit.GridCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureGridCnt % (tType, gridNum))
            tTypeInfo.GridLimitCntList.append(gridLimit)
        tTypeInfo.GridLimitCnt = len(tTypeInfo.GridLimitCntList)
        tTypeInfo.WishLibList = []
        wishLibSelect = setIpyData.GetWishLibSelect()
        for libIDStr, wishCnt in wishLibSelect.items():
            libID = int(libIDStr)
            wishLib = ChPyNetSendPack.tagMCTreasureWishLib()
            wishLib.LibID = libID
            wishLib.OutCntTotal = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishLibOut % (tType, libID))
            wishLib.IsUseWishCard = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishUseItem % (tType, libID))
            wishLib.WishList = []
            for wishIndex in range(wishCnt):
                wishID = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishSelect % (tType, libID, wishIndex))
                wish = ChPyNetSendPack.tagMCTreasureWish()
                wish.WishID = wishID
                wish.OutCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_TreasureWishOut % (tType, wishID))
                wishLib.WishList.append(wish)
            wishLib.WishCnt = len(wishLib.WishList)
            tTypeInfo.WishLibList.append(wishLib)
        tTypeInfo.WishLibCnt = len(tTypeInfo.WishLibList)
        treasureInfoPack.TreasuerInfoList.append(tTypeInfo)
    treasureInfoPack.InfoCount = len(treasureInfoPack.TreasuerInfoList)
    NetPackCommon.SendFakePack(curPlayer, treasureInfoPack)