hxp
5 天以前 26958aff1b844a743a805b4f9075bee800b72a46
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSuccess.py
@@ -2,544 +2,190 @@
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------
#
##@package Player.PlayerSuccess
#
# @todo:成就系统
# @author xdh
# @date 2018-04-23
# @author hxp
# @date 2025-10-17
# @version 1.0
#
#
# 详细描述: 成就系统
#
#---------------------------------------------------------------------
#"""Version = 2018-04-23 11:00"""
#---------------------------------------------------------------------
#-------------------------------------------------------------------------------
#"""Version = 2025-10-17 21:00"""
#-------------------------------------------------------------------------------
import ShareDefine
import ChConfig
import GameWorld
import ChPyNetSendPack
import ShareDefine
import NetPackCommon
import ItemCommon
import IPY_GameWorld
import PlayerControl
import ChPyNetSendPack
import ItemControler
import PlayerFamilyRedPacket
import IpyGameDataPY
import PyGameData
import EventReport
import ObjPool
import datetime
import time
import math
import ChEquip
import PlayerRune
import Operate_EquipStone
import Operate_EquipWash
import PlayerFeastRedPacket
import PlayerDogz
import DataRecordPack
## 获取成就字典信息值
#  @param curPlayer 玩家实例
#  @return
def GetPDictValue(curPlayer, key, defaultValue=0):
    return curPlayer.NomalDictGetProperty(key, defaultValue, ChConfig.Def_PDictType_Success)
## 设置成就字典信息值
#  @param curPlayer 玩家实例
#  @return
def SetPDictValue(curPlayer, key, value):
    PlayerControl.NomalDictSetProperty(curPlayer, key, value, ChConfig.Def_PDictType_Success)
    return
def GetSuccIsFinish(curPlayer, succID):
    #获取成就是否已完成
    return GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_Success_FinishTime, succID)
def SetSuccFinish(curPlayer, succID, finish=True):
    #设置成就是否完成
    return GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_Success_FinishTime, succID, finish)
#成就奖励是否已领取
def GetSuccHasGot(curPlayer, succID):
    #获取成就奖励是否已领取
    return GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_Success_AwardRecord, succID)
    return GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_SuccessAward, succID)
def SetSuccHasGot(curPlayer, succID, hasGot=True):
    #设置成就奖励领取状态
    if GetSuccHasGot(curPlayer, succID) == hasGot:
        return
    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_Success_AwardRecord, succID, hasGot)
    if hasGot:
        #已领取的可设置是否已完成为0
        SetSuccFinish(curPlayer, succID, 0)
        Sync_SuccTypeIndexAwardRecord(curPlayer, [succID], True) #设置成未领取的在外层同步
    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_SuccessAward, succID, hasGot)
    return 
def GetSuccPassportAwardHasGot(curPlayer, succID):
    #获取成就通行证奖励是否已领取
    return GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_Success_PassportAward, succID)
def SetSuccPassportAwardHasGot(curPlayer, succID, hasGot=True):
    #设置成就通行证奖励领取状态
    GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_Success_PassportAward, succID, hasGot)
    if hasGot:
        Sync_SuccTypeIndexAwardRecord(curPlayer, [succID])
    return
    
def SetSuccFinishValue(curPlayer, succType, condition, value):
    #设置成就完成度
    key = ChConfig.Def_PDict_Success_CntValue % (succType, condition)
    SetPDictValue(curPlayer, key, value)
#成就完成进度
def GetSuccValue(curPlayer, succType, conds):
    condition = "" if not conds else str(conds).replace(" ", "")[1:-1]
    return curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_SuccessValue % (succType, condition))
def SetSuccValue(curPlayer, succType, conds, value):
    condition = "" if not conds else str(conds).replace(" ", "")[1:-1]
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_SuccessValue % (succType, condition), value)
    return
def GetSuccFinishValue(curPlayer, succType, condition):
    #获取完成度
    key = ChConfig.Def_PDict_Success_CntValue % (succType, condition)
    return GetPDictValue(curPlayer, key)
## 成就配表数据类
class SuccessData():
    def __init__(self):
        self.succID = 0  #成就ID
        self.succType = 0 # 成就类型
        self.needCnt = 0 # 所以计数数量
        self.preSuccIDList = [] # 前置成就ID
        self.condition = [] # 辅助条件(根据类型自定义配置)
        self.awardItemDict = {} # 完成成就可领取的奖励物品{itemid:itemcnt}
        self.awardItemDict2 = {} # 完成成就可领取的奖励物品,通行证奖励{itemid:itemcnt}
        self.moneyDict = {} # 完成成就获得的金钱 {moneyType:money}
        self.exp = 0 # 完成成就获得的经验
        self.attrDict = {} # 完成成就获得的属性 {attrid:attrvalue}
        self.hasAward = True #是否有奖励
        self.redPacketID = 0 # 红包ID
        return
## 成就数据管理类
class SuccessDataMng():
    ## 构造函数
    #  @param None
    #  @return
    def __init__(self):
        self.succTypeConditionDict = {} #{succType:[condition,..]}
        self.successDataDict = {} # {succType:{index:SuccessData(),...}, ...}
        return
    ## 加载成就数据
    #  @param succType: 成就类型
    #  @return
    def __LoadSuccessData(self, succType):
        if succType in self.successDataDict:
            return
        successDataList = IpyGameDataPY.GetIpyGameDataByCondition('Success', {'Type':succType}, True, False)
        if not successDataList:
            return
        successDataObjDict = {}
        conditionDict = {}
        for successIpyData in successDataList:
            succData = SuccessData()
            succData.succID = successIpyData.GetID()
            succData.succType = succType
            succData.needCnt = successIpyData.GetNeedCnt()
            succData.preSuccIDList = list(successIpyData.GetPreSuccess())
            succData.condition = list(successIpyData.GetCondition())
            succData.awardItemDict = successIpyData.GetAwardItem()
            succData.awardItemDict2 = successIpyData.GetAwardItem2()
            succData.moneyDict = successIpyData.GetMoney()
            succData.exp = successIpyData.GetExp()
            succData.attrDict = successIpyData.GetAwardAttr()
            succData.redPacketID = successIpyData.GetRedPacketID()
            succData.hasAward = bool(succData.awardItemDict or succData.moneyDict or succData.exp or succData.attrDict or succData.redPacketID \
                                     or succData.awardItemDict2)
            successDataObjDict[succData.succID]=succData
            if tuple(succData.condition) not in conditionDict:
                conditionDict[tuple(succData.condition)] = [succData.succID]
            else:
                conditionDict[tuple(succData.condition)].append(succData.succID)
        self.successDataDict[succType] = successDataObjDict
        #self.succTypeConditionDict[succType] = conditionDict
        return
    ## 根据成就类型获取该类型所有成就
    #  @param succType: 成就类型
    #  @return: {index:SuccessData(), ...}
    def GetSuccDataByType(self, succType):
        if succType not in self.successDataDict:
            self.__LoadSuccessData(succType)
        return self.successDataDict.get(succType, {}).values()
    ## 根据成就类型获取该类型所有条件
    #  @param succType: 成就类型
    #  @return: {index:SuccessData(), ...}
#    def GetConditionDictByType(self, succType):
#        if succType not in self.succTypeConditionDict:
#            self.__LoadSuccessData(succType)
#        return self.succTypeConditionDict.get(succType, {})
    ## 根据成就类型索引获取某个成就数据
    #  @param succType: 成就类型
    #  @param index: 成就索引
    #  @return: SuccessData() or None
    def GetSuccessData(self, succID):
        succIpyData = IpyGameDataPY.GetIpyGameDataNotLog('Success', succID)
        if not succIpyData:
            return
        succType = succIpyData.GetType()
        if succType not in self.successDataDict:
            self.__LoadSuccessData(succType)
        return self.successDataDict.get(succType, {}).get(succID)
g_succDataMng = SuccessDataMng()
## 获取成就数据管理器
#  @param None
#  @return
def GetSuccDataMng():
    return g_succDataMng
## 成就OnLogin
#  @param curPlayer
#  @return
def SuccOnLogin(curPlayer):
    # 同步成就信息
    Sync_SuccessInfo(curPlayer)
    # 同步成就领奖记录
    Sync_SuccTypeIndexAwardRecord(curPlayer)
    # 检查老玩家
    __CheckOldPlayerSuccess(curPlayer)
    # 检查达成与否(有些可能改过上限,然后玩家可以完成,上线统一做一次检查)
    for succType in ShareDefine.SuccessTypeList:
        succInfoList = GetSuccDataMng().GetSuccDataByType(succType)
        if not succInfoList:
            continue
        __DoCheckSuccessFinish(curPlayer, succType, succInfoList)
    if curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Success_ScoreAward):
        Sync_SuccessScoreAward(curPlayer)
    SyncSuccessInfo(curPlayer)
    SyncSuccessAwardRecord(curPlayer)
    return
## 检查老玩家成就激活情况
#  @param None
#  @return
def __CheckOldPlayerSuccess(curPlayer):
    ''' 成就版本更新老玩家检查  每次启动服务后 玩家第一次上线检查成就完成情况
    '''
    initGameWorldTime = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_InitGameWorldTime)
    # 上线需要检查老玩家成就完成情况类型
    NeedCheckSuccTypeList = [
                             ]
    curCheckVersion = GetPDictValue(curPlayer, ChConfig.Def_PDict_Success_CheckVersion)
    if curCheckVersion == initGameWorldTime:
        GameWorld.DebugLog('    本次开启服务器处理过成就检查,不再处理 ', curPlayer.GetID())
def ResetSuccessByType(curPlayer, succType, ignoreFinish=True):
    #重置某类型成就进度,一般用于先重置次数再重新计数,或者活动类
    ipyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("Success", succType)
    if not ipyDataList:
        return
    GameWorld.DebugLog("更新老玩家上线检查成就curCheckVersion=%s" % (curCheckVersion))
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for succType in NeedCheckSuccTypeList:
        pass
    DataRecordPack.DR_CheckOldPlayerSuccess(curPlayer)
    SetPDictValue(curPlayer, ChConfig.Def_PDict_Success_CheckVersion, initGameWorldTime)
    GameWorld.DebugLog("更新老玩家上线检查成就updCheckVersions=%s" % (initGameWorldTime), curPlayer.GetID())
    for ipyData in ipyDataList:
        succID = ipyData.GetSuccID()
        if ignoreFinish and GetSuccHasGot(curPlayer, succID):
            continue
        conds = ipyData.GetCondition()
        SetSuccValue(curPlayer, succType, conds, 0)
    return
## 成就OnWeek
#  @param curPlayer
#  @return
def OnWeek(curPlayer):
    resetList = []
    weekResetSuccTypeList = IpyGameDataPY.GetFuncEvalCfg('WeekResetSuccType')
    for succType in weekResetSuccTypeList:
        succInfoList = GetSuccDataMng().GetSuccDataByType(succType)
        if not succInfoList:
            continue
        for succDataObj in succInfoList:
            succID = succDataObj.succID
            condition = succDataObj.condition
            if not GetSuccFinishValue(curPlayer, succType, condition):
                continue
            SetSuccFinishValue(curPlayer, succType, condition, 0)
            SetSuccFinish(curPlayer, succID, False)
            SetSuccHasGot(curPlayer, succID, False)
            resetList.append(succID)
            GameWorld.DebugLog("    OnWeek 重置成就类型: succID=%s" % (succID))
    if resetList:
        Sync_SuccTypeIndexAwardRecord(curPlayer, resetList, True)
        Sync_SuccessInfo(curPlayer, resetList, True)
    return
#def UpdateSuccessProgressByConditions(curPlayer, successType, conditionCountDict):
#    ## 根据多种条件更新进度,如装备多品质的
#    ipyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("Success", successType)
#    if not ipyDataList:
#        return
#    updIDList = []
#    updsuccDataList = []
#    updConditionDict = {}
#
#    for condition, newCount in conditionCountDict.items():
#        addCondList = [] # 不同的成就ID条件可能相同,只是最大进度不同,故传入的条件计数针对相同成就条件只能累加一次
#        for ipyData in ipyDataList:
#            succID = ipyData.GetSuccID()
#            conds = ipyData.GetCondition()
#            needCnt = ipyData.GetNeedCnt()
#
#            tupleCond = tuple(conds) # 作为字典key用
#
#            if tupleCond not in updConditionDict:
#                updConditionDict[tupleCond] = [conds, 0, 0] # [条件, 更新值, 最大进度值]
#            updInfo = updConditionDict[tupleCond]
#            if updInfo[2] < needCnt:
#                updInfo[2] = needCnt
#
#            if not __CheckCanAddSuccess(curPlayer, ipyData, list(condition)):
#                continue
#
#            if succID not in updIDList:
#                updIDList.append(succID)
#                updsuccDataList.append(ipyData)
#
#            if tupleCond not in addCondList:
#                addCondList.append(tupleCond)
#                updConditionDict[tupleCond][1] = updConditionDict[tupleCond][1] + newCount # 更新进度值
#
#    # 没有找到更新目标不处理
#    #GameWorld.DebugLog("    updConditionDict=%s" % updConditionDict)
#    #GameWorld.DebugLog("    updIDList=%s" % updIDList)
#    if not updIDList:
#        return
#
#    # 先更新成就记录值后再判断完成与否
#    for cond, updCnt, maxCnt in updConditionDict.values():
#        if not updCnt:
#            continue
#        updCnt = min(maxCnt, updCnt)
#        if GetSuccValue(curPlayer, successType, cond) == updCnt:
#            continue
#        SetSuccValue(curPlayer, successType, cond, updCnt)
#    SyncSuccessInfo(curPlayer, syncTypeCondList)
#    return
def DoResetSuccessIDList(curPlayer, resetSuccIDList):
    ## 重置成就相关数据
    if not resetSuccIDList:
        return
    resetList = []
    for succID in resetSuccIDList:
        succData = GetSuccDataMng().GetSuccessData(succID)
        if not succData:
            continue
        succType = succData.succType
        condition = succData.condition
        #if not GetSuccFinishValue(curPlayer, succType, condition):
        #    continue
        SetSuccFinishValue(curPlayer, succType, condition, 0)
        SetSuccFinish(curPlayer, succID, False)
        SetSuccHasGot(curPlayer, succID, False)
        resetList.append(succID)
        GameWorld.DebugLog("    重置成就类型: succType=%s,succID=%s" % (succType, succID))
    if resetList:
        Sync_SuccTypeIndexAwardRecord(curPlayer, resetList, True)
        Sync_SuccessInfo(curPlayer, resetList, True)
    return
## 成就OnDay
#  @param curPlayer
#  @return
def SuccOnDay(curPlayer):
    resetList = []
    # 重置连续类型断掉的数据
    serverDay = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_ServerDay)
    GameWorld.DebugLog("连续类型成就过天...serverDay=%s" % serverDay)
    for succType in ShareDefine.ContinueSuccessTypeList:
        succInfoList = GetSuccDataMng().GetSuccDataByType(succType)
        if not succInfoList:
            continue
        for succDataObj in succInfoList:
            succID = succDataObj.succID
            condition = succDataObj.condition
            #GameWorld.DebugLog("    succID=%s" % (succID))
            finishTime = GetSuccIsFinish(curPlayer, succID)
            # 已完成的不再检查
            if finishTime > 0 or GetSuccHasGot(curPlayer, succID):
                GameWorld.DebugLog("        已完成的不再检查")
                continue
            if __DoCheckResetContinue(curPlayer, serverDay, succID):
                SetSuccFinishValue(curPlayer, succType, condition, 0)
                resetList.append(succID)
    if resetList:
        Sync_SuccessInfo(curPlayer, resetList, True)
    return
## 检查成就连续天情况
#  @param curPlayer
#  @return True-连续已中断
def __DoCheckResetContinue(curPlayer, serverDay, succID):
    lastDayKey = ChConfig.Def_PDict_Success_LastDay % (succID)
    lastDay = GetPDictValue(curPlayer, lastDayKey)
    # 记录时间从开服天数改成 time.time()
    if lastDay <= 1000:
        #GameWorld.DebugLog("        lastDay <= 0")
        return
    tick = int(time.time())
    serverDay = GameWorld.ChangeTimeNumToDatetime(tick, ChConfig.TYPE_Time_Format_Day)
    lastDay = GameWorld.ChangeTimeNumToDatetime(lastDay, ChConfig.TYPE_Time_Format_Day)
    # 昨天有更新的不处理,证明还是连续状态
    if lastDay == serverDay - datetime.timedelta(1):
        GameWorld.DebugLog("        昨天有更新的不处理,证明还是连续状态")
        return
    SetPDictValue(curPlayer, lastDayKey, 0)
    GameWorld.DebugLog("    重置成就连续类型: succID=%s" % (succID))
    return True
def ResetSuccessByType(curPlayer, succType):
    #重置某类型成就进度(已完成的不重置)
    succInfoList = GetSuccDataMng().GetSuccDataByType(succType)
    if not succInfoList:
        return
    #先重置次数再重新计数
    for succDataObj in succInfoList:
        succID = succDataObj.succID
        condition = succDataObj.condition
        #GameWorld.DebugLog("    succID=%s" % (succID))
        finishTime = GetSuccIsFinish(curPlayer, succID)
        # 已完成的不再检查
        if finishTime > 0 or GetSuccHasGot(curPlayer, succID):
            continue
        SetSuccFinishValue(curPlayer, succType, condition, 0)
    return
def UpdateSuccessProgressByConditions(curPlayer, successType, conditionCountDict):
    if successType not in ShareDefine.SuccessTypeList:
        return
    succInfoList = GetSuccDataMng().GetSuccDataByType(successType)
    if not succInfoList:
        return
    updIDList = []
    updsuccDataList = []
    updConditionDict = {}
    for condition, newCount in conditionCountDict.items():
        addCondList = [] # 不同的成就ID条件可能相同,只是最大进度不同,故传入的条件计数针对相同成就条件只能累加一次
        for succDataObj in succInfoList:
            cond = succDataObj.condition
            tupleCond = tuple(cond) # 作为字典key用
            succID = succDataObj.succID
            needCnt = succDataObj.needCnt
            if tupleCond not in updConditionDict:
                updConditionDict[tupleCond] = [cond, 0, 0] # [条件, 更新值, 最大进度值]
            updInfo = updConditionDict[tupleCond]
            if updInfo[2] < needCnt:
                updInfo[2] = needCnt
            if not __CheckCanAddSuccess(curPlayer, succDataObj, list(condition)):
                continue
            if succID not in updIDList:
                updIDList.append(succID)
                updsuccDataList.append(succDataObj)
            if tupleCond not in addCondList:
                addCondList.append(tupleCond)
                updConditionDict[tupleCond][1] = updConditionDict[tupleCond][1] + newCount # 更新进度值
    # 没有找到更新目标不处理
    #GameWorld.DebugLog("    updConditionDict=%s" % updConditionDict)
    #GameWorld.DebugLog("    updIDList=%s" % updIDList)
    if not updIDList:
        return
    isUpd = False
    # 先更新成就记录值后再判断完成与否
    for cond, updCnt, maxCnt in updConditionDict.values():
        if not updCnt:
            continue
        updCnt = min(maxCnt, updCnt)
        if GetSuccFinishValue(curPlayer, successType, cond) == updCnt:
            continue
        isUpd = True
        SetSuccFinishValue(curPlayer, successType, cond, updCnt)
        #GameWorld.DebugLog("    successType=%s,cond=%s,updCnt=%s,maxCnt=%s"  % (successType, cond, updCnt, maxCnt))
    if not isUpd:
        return
    # 同步更新信息
    Sync_SuccessInfo(curPlayer, updIDList, False)
    # 更新值后检查成就完成情况
    __DoCheckSuccessFinish(curPlayer, successType, updsuccDataList)
    return
## 更新成就完成进度逻辑(外部功能调用,只适用于不重置的成就类型)
#  @param curPlayer
#  @param successType: 成就类型
#  @param newCnt: 新进度值
#  @return
def UptateSuccessProgress(curPlayer, successType, newCnt, condition=[]):
    if successType not in ShareDefine.SuccessTypeList:
    ipyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("Success", successType)
    if not ipyDataList:
        return
    if successType in ShareDefine.FeastRedPackSuccessTypeList:
        if not PlayerFeastRedPacket.GetFeastRedPacketState():
            return
    succInfoList = GetSuccDataMng().GetSuccDataByType(successType)
    if not succInfoList:
        GameWorld.DebugLog("    找不到成就数据successType=%s" % successType)
        return
    curCnt = -1
    for succData in succInfoList:
        if not __CheckCanAddSuccess(curPlayer, succData, condition):
    curCnt = None
    for ipyData in ipyDataList:
        if not __CheckCanAddSuccess(curPlayer, ipyData, condition):
            continue
        curCnt = GetSuccFinishValue(curPlayer, successType, succData.condition)
        curCnt = GetSuccValue(curPlayer, successType, ipyData.GetCondition())
        break
    if curCnt == -1:
    if curCnt == None or newCnt <= curCnt:
        #GameWorld.DebugLog("不需要更新成就值: curCnt=%s" % (curCnt))
        return
    if newCnt > curCnt: #这里需要立即更新!!
        DoAddSuccessProgress(curPlayer, successType, newCnt - curCnt, condition, False)
    #update的需要立即更新!!
    DoAddSuccessProgress(curPlayer, successType, newCnt - curCnt, condition, False)
    return
def __CheckCanAddSuccess(curPlayer, succDataObj, condition):
    successType = succDataObj.succType
    succID = succDataObj.succID
    cond = succDataObj.condition
    if successType in ShareDefine.FeastRedPackSuccessTypeList:
        todayFeastSuccIDList = PlayerFeastRedPacket.GetTodayFeastSuccIDList()
        if not todayFeastSuccIDList or succID not in todayFeastSuccIDList:
            #GameWorld.DebugLog("    非今日节日红包成就,不增加进度! succID=%s,todayFeastSuccIDList=%s" % (succID, todayFeastSuccIDList))
            return
    isUnDownCheck = successType in ShareDefine.UnDownCheckSuccessTypeList
    isContain = successType in ShareDefine.ContainSuccessTypeList
def __CheckCanAddSuccess(curPlayer, ipyData, condition):
    ## 检查可否添加进度
    succID = ipyData.GetSuccID()
    # 已完成的不再检查
    if GetSuccIsFinish(curPlayer, succID) or GetSuccHasGot(curPlayer, succID):
    if GetSuccHasGot(curPlayer, succID):
        return
    #前置成就判断
    preSuccIDList = succDataObj.preSuccIDList
    for preSuccID in preSuccIDList:
        if not GetSuccIsFinish(curPlayer, preSuccID) and not GetSuccHasGot(curPlayer, preSuccID):
            return
    
    # 有辅助条件的, 需判断是否向下检查
    if len(cond) > 0:
        if isContain:
            if condition[0] not in cond:
    successType = ipyData.GetSuccType()
    conds = ipyData.GetCondition()
    if not conds:
        # 无条件的默认可添加
        return True
    if not condition:
        return
    # 传进来的条件满足配置的条件列表中的一个就行的成就类型
    if successType in ShareDefine.ContainSuccessTypeList:
        if condition[0] not in conds:
            return
    # 传进来的条件是配置条件的整数倍的成就类型
    elif successType in ShareDefine.MultipleSuccessTypeList:
        if not (condition[0] / conds[0] and condition[0] % conds[0] == 0):
            #必须是条件的倍数
            return
    else:
        if len(conds) != len(condition):
            return
        # 不向下检查增加进度的, 仅配置值等于该值的可增加进度
        if successType in ShareDefine.UnDownCheckSuccessTypeList:
            if conds != condition:
                return
        elif successType in ShareDefine.MultipleSuccessTypeList:
            if not (condition[0]/cond[0] and condition[0]%cond[0] == 0):
                #必须是条件的倍数
                return
        # 可向下增加进度的, 仅配置值 <= 该值的可增加进度
        else:
            if len(cond) != len(condition):
                return
            # 不向下检查增加进度的, 仅配置值等于该值的可增加进度
            if isUnDownCheck and cond != condition:
                return
            # 可向下增加进度的, 仅配置值 <= 该值的可增加进度
            if not isUnDownCheck:
                isbreak = False
                undowncheckIndexList = [] # 不向下检查的条件索引
                if successType in ShareDefine.PartUnDownCheckSuccessTypeInfo:
                    undowncheckIndexList = ShareDefine.PartUnDownCheckSuccessTypeInfo[successType]
                for i, num in enumerate(cond):
                    if i in undowncheckIndexList:
                        if num != condition[i]:
                            isbreak = True
                            break
                    if num > condition[i]:
            isbreak = False
            undowncheckIndexList = [] # 不向下检查的条件索引
            if successType in ShareDefine.PartUnDownCheckSuccessTypeInfo:
                undowncheckIndexList = ShareDefine.PartUnDownCheckSuccessTypeInfo[successType]
            for i, num in enumerate(conds):
                if i in undowncheckIndexList:
                    if num != condition[i]:
                        isbreak = True
                        break
                if isbreak:
                    return
                if num > condition[i]:
                    isbreak = True
                    break
            if isbreak:
                return
    return True
# 将攻击类的成就一次执行,减少运行量
def FinishDelayAddSuccessProgress(curPlayer, tick, isFinish=True):
    # 将攻击类的成就一次执行,减少运行量
    if not isFinish:
        # 二次延迟处理
@@ -555,401 +201,131 @@
    
    successDict = PyGameData.g_delaySuccessDict.pop(playerID)
    for sucessInfo, cnt in successDict.items():
        DoAddSuccessProgress(curPlayer, sucessInfo[0], cnt, list(sucessInfo[1]), False)
    return
## 增加成就完成进度逻辑(外部功能调用)
#  @param curPlayer
#  @param successType: 成就类型
#  @param addCnt: 增加进度值
#  @param condition: 扩展条件
#  @return
def DoAddSuccessProgress(curPlayer, successType, addCnt, condition=[], delayCalc=True):
    #GameWorld.DebugLog("DoAddSuccessProgress type=%s,addCnt=%s,condition=%s"
    #                   % (successType, addCnt, condition), curPlayer.GetPlayerID())
    if GameWorld.IsCrossServer():
        return
    
    if successType in ShareDefine.FeastRedPackSuccessTypeList:
        if not PlayerFeastRedPacket.GetFeastRedPacketState():
            #GameWorld.DebugLog("非节日红包活动时间,不增加成就!successType=%s" % successType)
            return
    playerID = curPlayer.GetID()
    if delayCalc and successType not in ShareDefine.NeedResetSuccessTypeList:
    if delayCalc:
        if playerID not in PyGameData.g_delaySuccessDict:
            PyGameData.g_delaySuccessDict[playerID] = {}
        # 最终处理减少计算量
        # 最终处理减少计算量,短时间多次累加汇总一次计算
        successTuple = (successType, tuple(condition))
        if successTuple not in PyGameData.g_delaySuccessDict[playerID]:
            PyGameData.g_delaySuccessDict[playerID][successTuple] = addCnt
        else:
            PyGameData.g_delaySuccessDict[playerID][successTuple] = PyGameData.g_delaySuccessDict[playerID][successTuple] + addCnt
        return
    if condition and type(condition) != type([]):
        GameWorld.ErrLog('DoAddSuccessProgress type=%s, condition=%s 错误!'%(type, condition))
        GameWorld.ErrLog('DoAddSuccessProgress type=%s, condition=%s 错误!' % (type, condition))
        return
    if successType not in ShareDefine.SuccessTypeList:
        return
    
    succInfoList = GetSuccDataMng().GetSuccDataByType(successType)
    if not succInfoList:
        GameWorld.DebugLog("    找不到成就数据successType=%s" % successType)
    ipyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("Success", successType)
    if not ipyDataList:
        GameWorld.DebugLog("找不到成就数据successType=%s" % successType)
        return
    
    maxCnt = 0 # 本次可以更新到的最大值
    updConditionList = [] # 需更新进度值的key编号列表
    updIDList = []
    updsuccDataList = []
    tick = int(time.time())
    for succDataObj in succInfoList:
        succID = succDataObj.succID
        needCnt = succDataObj.needCnt
    for ipyData in ipyDataList:
        
        if not __CheckCanAddSuccess(curPlayer, succDataObj, condition):
        if not __CheckCanAddSuccess(curPlayer, ipyData, condition):
            continue
        updIDList.append(succID)
        updsuccDataList.append(succDataObj)
        curCondition = succDataObj.condition
        if curCondition not in updConditionList:
            updConditionList.append(curCondition)
        conds = ipyData.GetCondition()
        if conds not in updConditionList:
            updConditionList.append(conds)
            
        needCnt = ipyData.GetNeedCnt()
        if maxCnt < needCnt:
            maxCnt = needCnt
            
        # 连续类型的, 更新连续时的开服天数
        if successType in ShareDefine.ContinueSuccessTypeList:
            lastDayKey = ChConfig.Def_PDict_Success_LastDay % (succID)
            SetPDictValue(curPlayer, lastDayKey, tick)
    #GameWorld.DebugLog("    updConditionList=%s,maxCnt=%s" % (str(updConditionList), maxCnt))
    # 没有找到更新目标不处理
    if not updConditionList or maxCnt <= 0:
        return
    
    # 先更新成就记录值后再判断完成与否
    for condition in updConditionList:
        curCnt = GetSuccFinishValue(curPlayer, successType, condition)
    syncTypeCondList = []
    for conds in updConditionList:
        curCnt = GetSuccValue(curPlayer, successType, conds)
        updCnt = min(maxCnt, curCnt + addCnt)
        SetSuccFinishValue(curPlayer, successType, condition, updCnt)
#        GameWorld.DebugLog("    successType=%s,condition=%s,curCnt=%s,addCnt=%s,updCnt=%s"
#                           % (successType,condition, curCnt, addCnt, updCnt))
    # 同步更新信息
    Sync_SuccessInfo(curPlayer, updIDList, False)
    # 更新值后检查成就完成情况
    __DoCheckSuccessFinish(curPlayer, successType, updsuccDataList)
        SetSuccValue(curPlayer, successType, conds, updCnt)
        syncTypeCondList.append([successType, conds])
    SyncSuccessInfo(curPlayer, syncTypeCondList)
    return
## 检查成就完成情况
#  @param curPlayer
#  @param successType: 成就类型
#  @param succInfoDict: 该类型所有成就信息字典
#  @param isPub: 进度值是否公共
#  @param updNumList: 有更新进度的编号列表
#  @return
def __DoCheckSuccessFinish(curPlayer, successType, succInfoList):
def GetSuccessAward(curPlayer, succID):
    ipyData = IpyGameDataPY.GetIpyGameDataByCondition("Success", {"SuccID":succID})
    if not ipyData:
        return
    
    isNeedSys = successType in [ShareDefine.SuccType_ElderBattlefieldKill, ShareDefine.SuccType_ElderBattlefieldConKill]
    curTime = GameWorld.ChangeTimeStrToNum(GameWorld.GetCurrentDataTimeStr())
    playerName = curPlayer.GetName()
    for succDataObj in succInfoList:
        succID = succDataObj.succID
        needCnt = succDataObj.needCnt
        finishTime = GetSuccIsFinish(curPlayer, succID)
        # 已完成的不再检查
        if finishTime > 0 or GetSuccHasGot(curPlayer, succID):
            continue
        curCnt = GetSuccFinishValue(curPlayer, successType, succDataObj.condition)
        # 完成成就
        if curCnt >= needCnt:
            # 设置完成时间
            SetSuccFinish(curPlayer, succID)
            # 没有奖励的直接设置已领取
            if not succDataObj.hasAward:
                SetSuccHasGot(curPlayer, succID)
            # 通知客户端完成成就
            Notify_FinishSuccess(curPlayer, succID, curTime)
            if isNeedSys:
                PlayerControl.WorldNotify(0, 'AncientBattlefield_1', [playerName, succID])
            GameWorld.DebugLog("    完成成就succID=%s, type=%s,time=%s"
                               % (succID, successType, curTime))
    if GetSuccHasGot(curPlayer, succID):
        GameWorld.DebugLog("该成就奖励已经领取过! succID=%s" % succID)
        return
    
    succType = ipyData.GetSuccType()
    conds = ipyData.GetCondition()
    needCnt = ipyData.GetNeedCnt()
    curValue = GetSuccValue(curPlayer, succType, conds)
    if curValue < needCnt:
        GameWorld.DebugLog("该成就未完成! succID=%s,curValue=%s < %s" % (succID, curValue, needCnt))
        return
    SetSuccHasGot(curPlayer, succID)
    SyncSuccessAwardRecord(curPlayer, [succID], True)
    awardItemList = ipyData.GetAwardItemList()
    if awardItemList:
        ItemControler.GivePlayerItemOrMail(curPlayer, awardItemList, event=["SuccessAward", False, {}])
    return
## 计算成就属性
#  @param curPlayer 玩家
#  @param allAttrList 属性列表
#  @return None
def CalcSuccessAttr(curPlayer):
    allAttrList = [{} for _ in range(4)]
    attrDict = {}
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for i in xrange(ipyDataMgr.GetSuccessCount()):
        ipyData = ipyDataMgr.GetSuccessByIndex(i)
        succID = ipyData.GetID()
        attrAwardDict = ipyData.GetAwardAttr()
        if not attrAwardDict:
            continue
        if not GetSuccHasGot(curPlayer, succID):
            continue
        for attrID, attrValue in attrAwardDict.items():
            attrDict[attrID] = attrDict.get(attrID, 0) + attrValue
    #GameWorld.DebugLog("    成就增加属性 attrDict=%s" % (attrDict))
    for attrID, attrValue in attrDict.items():
        PlayerControl.CalcAttrDict_Type(attrID, attrValue, allAttrList)
    PlayerControl.SetCalcAttrListValue(curPlayer, ChConfig.Def_CalcAttrFunc_Success, allAttrList)
    return
## 刷新成就属性
def RefreshSuccessAttr(curPlayer):
    CalcSuccessAttr(curPlayer)
    # 不立即刷新
    PlayerControl.PlayerControl(curPlayer).RefreshPlayerAttrState()
    return
## 同步成就信息
#  @param curPlayer:
#  @param syncDict: 指定同步字典
#  @param isSendZero: 是否同步空数据(如连续类型进度值被清空时 or GM命令测试), 数据比较多,空数据默认就不同步了
#  @return
def Sync_SuccessInfo(curPlayer, succIdList=[], isSendZero=False):
    succInfoListPack = ChPyNetSendPack.tagMCSuccessInfoList()
    succInfoListPack.Clear()
    succInfoListPack.SuccessInfoList = []
    if succIdList:
        syncIdList = succIdList
    else:
        syncIdList = []
def SyncSuccessInfo(curPlayer, syncTypeCondList=None, isSendZero=False):
    if not syncTypeCondList:
        syncTypeCondList = []
        ipyDataMgr = IpyGameDataPY.IPY_Data()
        for i in xrange(ipyDataMgr.GetSuccessCount()):
            ipyData = ipyDataMgr.GetSuccessByIndex(i)
            succID = ipyData.GetID()
            syncIdList.append(succID)
        for index in range(ipyDataMgr.GetSuccessCount()):
            ipyData = ipyDataMgr.GetSuccessByIndex(index)
            # 已领取的不发
            if GetSuccHasGot(curPlayer, ipyData.GetSuccID()):
                continue
            succType = ipyData.GetSuccType()
            conds = ipyData.GetCondition()
            tcList = [succType, conds]
            if tcList in syncTypeCondList:
                continue
            syncTypeCondList.append(tcList)
            
    syncTypeDict = {}
    for succID in syncIdList:
        succData = GetSuccDataMng().GetSuccessData(succID)
        if not succData:
            continue
        succType = succData.succType
        condition = succData.condition
        CntValue = GetSuccFinishValue(curPlayer, succType, condition)
        isfinish = GetSuccIsFinish(curPlayer, succID)
    successInfoList = []
    objPoolMgr = ObjPool.GetPoolMgr()
    for succType, conds in syncTypeCondList:
        curValue = GetSuccValue(curPlayer, succType, conds)
        # 不发送为0的数据
        if not isSendZero and CntValue <= 0 and not isfinish:
        if not isSendZero and curValue <= 0:
            continue
        # 已领取的不发
        if GetSuccHasGot(curPlayer, succID):
            continue
        if succType not in syncTypeDict:
            syncTypeDict[succType] = [[condition, CntValue]]
        elif [condition, CntValue] not in syncTypeDict[succType]:
            syncTypeDict[succType].append([condition, CntValue])
        succInfo = objPoolMgr.acquire(ChPyNetSendPack.tagSCSuccessInfo)
        succInfo.SuccType = succType
        succInfo.Conds = conds
        succInfo.CLen = len(succInfo.Conds)
        succInfo.CurValue = curValue
        successInfoList.append(succInfo)
        
    if not syncTypeDict:
    if not successInfoList:
        return
    
    for sType, conditionList in syncTypeDict.items():
        for condition, cntValue in conditionList:
            succInfo = ChPyNetSendPack.tagMCSuccessInfo()
            succInfo.Clear()
            succInfo.SuccType = sType
            succInfo.Condition = str(condition) if condition else '[0]'
            succInfo.Len = len(succInfo.Condition)
            succInfo.CntValue = cntValue
            succInfoListPack.SuccessInfoList.append(succInfo)
    succInfoListPack.count = len(succInfoListPack.SuccessInfoList)
    NetPackCommon.SendFakePack(curPlayer, succInfoListPack)
    clientPack = objPoolMgr.acquire(ChPyNetSendPack.tagSCSuccessInfoList)
    clientPack.SuccessInfoList = successInfoList
    clientPack.Count = len(clientPack.SuccessInfoList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
## 通知完成成就
#  @param None
#  @return
def Notify_FinishSuccess(curPlayer, succID, finishTime):
    succFinishPack = ChPyNetSendPack.tagMCSuccessFinish()
    succFinishPack.Clear()
    succFinishPack.SuccID = succID
    #succFinishPack.FinishTime = finishTime
    NetPackCommon.SendFakePack(curPlayer, succFinishPack)
    return
def GetCanGetAwardSuccByType(curPlayer, successTypeList):
    ##根据类型获取可领取的成就
    succList = []
    for successType in successTypeList:
        succInfoList = GetSuccDataMng().GetSuccDataByType(successType)
        for succDataObj in succInfoList:
            succID = succDataObj.succID
            if GetSuccIsFinish(curPlayer, succID) and not GetSuccHasGot(curPlayer, succID):
                succList.append(succID)
    return succList
def GiveSuccAward(curPlayer, succIDList, isGiveItem=True):
    #给成就奖励
    awardDict = {}
    for succID in succIDList:
        awardItemDict = __DoGetSuccTypeIndexAward(curPlayer, succID, isGiveItem)
        if not awardItemDict:
            continue
        for itemID,itemCnt in awardItemDict.items():
            awardDict[itemID] = awardDict.get(itemID, 0)+itemCnt
    return awardDict
def GetSuccessPassportState(curPlayer):
    ## 成就通行证状态
    return curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CTGGoodsBuyCount % ChConfig.Def_CTGID_SuccessPassport) > 0
#// A5 42 领取成就奖励 #tagMCGetSuccessAward
#
#struct    tagMCGetSuccessAward
#{
#    tagHead        Head;
#    DWORD        SuccID;    //成就ID
#    BYTE        IsPassport;    //是否通行证奖励
#};
def OnGetSuccessAward(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    succID = clientData.SuccID
    isPassport = clientData.IsPassport
    __DoGetSuccTypeIndexAward(curPlayer, succID, isPassport=isPassport)
    return
## 领取某个成就奖励
#  @param None
#  @return
def __DoGetSuccTypeIndexAward(curPlayer, succID, isGiveItem=True, isPassport=False):
    GameWorld.DebugLog("领取成就奖励succID=%s,isPassport=%s" % (succID, isPassport))
    succData = GetSuccDataMng().GetSuccessData(succID)
    if not succData:
        GameWorld.DebugLog("    成就数据不存在!succID=%s" % (succID))
        return
    if not succData.hasAward:
        GameWorld.DebugLog("    该成就没有配置奖励!")
        return
    finishTime = GetSuccIsFinish(curPlayer, succID)
    hasGot = GetSuccHasGot(curPlayer, succID) # 因为普通奖励领取后会重置是否已完成,所以如果已经领取普通奖励也代表已完成
    if finishTime <= 0 and not hasGot:
        GameWorld.DebugLog("    该成就未完成!")
        return
    if isPassport:
        if GetSuccPassportAwardHasGot(curPlayer, succID):
            GameWorld.DebugLog("    该成就通行证奖励已经领取过!")
            return
        if not GetSuccessPassportState(curPlayer):
            GameWorld.DebugLog("    未开通成就通行证,无法领取通行证奖励!")
            return
        itemDict = succData.awardItemDict2
        if not itemDict:
            GameWorld.DebugLog("    该成就没用通行证奖励!")
            return
    else:
        if hasGot:
            GameWorld.DebugLog("    该成就奖励已经领取过!")
            return
        itemDict = succData.awardItemDict
#    if awardItemDict and '[' in str(awardItemDict):
#        #修行成就奖励根据境界等级变
#        curRealmLV = curPlayer.GetOfficialRank()
#        sortLVList=sorted(awardItemDict.keys())
#        findLV = sortLVList[0]
#        for realmlv in sortLVList:
#            if curRealmLV <= int(realmlv):
#                findLV = realmlv
#                break
#        itemDict = dict(awardItemDict[findLV])
#    else:
#        itemDict = awardItemDict
    # 检查背包
    if isGiveItem:
        if not ItemCommon.CheckPackEnough(curPlayer, itemDict):
            return
#        packSpace = ItemCommon.GetItemPackSpace(curPlayer, IPY_GameWorld.rptItem)
#        needSpace = len(itemDict)
#        if needSpace > packSpace:
#            PlayerControl.NotifyCode(curPlayer, "GeRen_chenxin_998371")
#            return
    # 更新领奖记录
    if isPassport:
        SetSuccPassportAwardHasGot(curPlayer, succID)
    else:
        SetSuccHasGot(curPlayer, succID)
    # 给物品
    giveItemList = []
    if isGiveItem:
        for itemID, itemCnt in itemDict.items():
            isPutIn = ItemControler.GivePlayerItem(curPlayer, itemID, itemCnt, 0, [IPY_GameWorld.rptItem, IPY_GameWorld.rptAnyWhere])
            if isPutIn:
                giveItemList.append([itemID, itemCnt])
    if isPassport:
        # 通行证奖励只给物品
        ItemControler.NotifyGiveAwardInfo(curPlayer, giveItemList, "SuccessAward")
        return itemDict
    #给钱
    for moneyType, value in succData.moneyDict.items():
        PlayerControl.GiveMoney(curPlayer, int(moneyType), value)
    #给经验
    if succData.exp:
        PlayerControl.PlayerControl(curPlayer).AddExp(succData.exp)
    #属性
    if succData.attrDict:
        RefreshSuccessAttr(curPlayer)
    #发红包
    if succData.redPacketID:
        PlayerFamilyRedPacket.CreatRedPacketByID(curPlayer, succData.redPacketID, PlayerFamilyRedPacket.State_NoGot, succID)
    GameWorld.DebugLog("    OK! awardItemDict=%s moneyDict=%s" % (itemDict, succData.moneyDict))
    ItemControler.NotifyGiveAwardInfo(curPlayer, giveItemList, "SuccessAward", exp=succData.exp, moneyInfo=succData.moneyDict)
    return itemDict
## 通知成就对应奖励领奖记录
#  @param None
#  @return
def Sync_SuccTypeIndexAwardRecord(curPlayer, succIDList=[], isSyncZero=False):
def SyncSuccessAwardRecord(curPlayer, succIDList=[], isSyncZero=False):
    ## 通知成就对应奖励领奖记录
    if succIDList:
        recordIndexList = []
        for succID in succIDList:
@@ -961,60 +337,25 @@
        succCnt = ipyDataMgr.GetSuccessCount()
        if not succCnt:
            return
        maxSuccid = ipyDataMgr.GetSuccessByIndex(succCnt-1).GetID()
        recordIndexList = xrange(maxSuccid / 31+1)
    succFARPack = ChPyNetSendPack.tagMCSuccessFinishAwardRecordList()
    succFARPack.Clear()
    succFARPack.RecordList = []
        maxSuccID = ipyDataMgr.GetSuccessByIndex(succCnt - 1).GetSuccID()
        recordIndexList = xrange(maxSuccID / 31 + 1)
    objPoolMgr = ObjPool.GetPoolMgr()
    recordList = []
    for i in recordIndexList:
        awardRecord=curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Success_AwardRecord%i)
        passportAwardRecord=curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Success_PassportAward%i)
        if not isSyncZero and not awardRecord and not passportAwardRecord:
        awardRecord = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_SuccessAward % i)
        if not isSyncZero and not awardRecord:
            continue
        recordInfo = ChPyNetSendPack.tagMCSuccessFinishAwardRecord()
        recordInfo = objPoolMgr.acquire(ChPyNetSendPack.tagSCSuccessAwardRecord)
        recordInfo.RecordIndex = i
        recordInfo.Record = awardRecord
        recordInfo.PassportRecord = passportAwardRecord
        succFARPack.RecordList.append(recordInfo)
        recordList.append(recordInfo)
        
    succFARPack.RecordCnt = len(succFARPack.RecordList)
    NetPackCommon.SendFakePack(curPlayer, succFARPack)
    return
def AddEnterFBSuccess(curPlayer, mapID, addCount):
    #进入副本成就相关
    return
def GetSuccessScoreAward(curPlayer, awardIndex):
    playerID = curPlayer.GetPlayerID()
    awardDict = IpyGameDataPY.GetFuncEvalCfg("SucceeScore", 1, {})
    if str(awardIndex) not in awardDict:
    if not recordList:
        return
    awardInfo = awardDict[str(awardIndex)]
    if len(awardInfo) != 2:
        return
    needScore, awardItemList = awardInfo
    if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_SuccessSocre, needScore):
        GameWorld.Log("成就积分不足,无法领奖! awardIndex=%s,needScore=%s" % (awardIndex, needScore), playerID)
        return
    awardState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Success_ScoreAward)
    if awardState & pow(2, awardIndex):
        GameWorld.DebugLog("已领取过该成就积分奖励! awardIndex=%s" % awardIndex, playerID)
        return
    updAwardState = awardState | pow(2, awardIndex)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_Success_ScoreAward, updAwardState)
    GameWorld.DebugLog("领取成就积分奖励! awardIndex=%s,awardState=%s,updAwardState=%s" % (awardIndex, awardState, updAwardState), playerID)
    Sync_SuccessScoreAward(curPlayer)
    ItemControler.GivePlayerItemOrMail(curPlayer, awardItemList, event=["SuccessScore", False, {}])
    return
def Sync_SuccessScoreAward(curPlayer):
    clientPack = ChPyNetSendPack.tagMCSuccessScoreInfo()
    clientPack.Clear()
    clientPack.ScoreAwardState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Success_ScoreAward)
    clientPack = objPoolMgr.acquire(ChPyNetSendPack.tagSCSuccessAwardRecordList)
    clientPack.RecordList = recordList
    clientPack.RecordCnt = len(clientPack.RecordList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return