ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/ItemCommon.py
@@ -304,7 +304,7 @@
    GameWorld.DebugLog("物品过期时间" + timeStr)
    return GameWorld.ChangeTimeStrToNum(timeStr)
def CreateSingleItem(itemID, itemCount=1, isAuctionItem=False, expireTime=0):
def CreateSingleItem(itemID, itemCount=1, isAuctionItem=False, expireTime=0, curPlayer=None, setAttrDict=None):
    ''' 创建物品
    @param isAuctionItem: 是否拍品,默认非拍品
    @param expireTime: 有效时间,时间单位由时效类型决定
@@ -356,9 +356,236 @@
    if expireTime > 0:
        curSingleItem.SetUserAttr(ShareDefine.Def_IudetExpireTime, expireTime)
        
    # 装备
    if GetIsEquip(curSingleItem):
        setAttrDict = GetCreateEquipAttr(curSingleItem, curPlayer, setAttrDict)
        if setAttrDict == None:
            curSingleItem.Clear()
            return
    AddCreateItemAttr(curSingleItem, setAttrDict)
    #这里返回的是SingleItem , 如果创建了,未使用,会找出C++内存泄露!!!
    return curSingleItem
def AddCreateItemAttr(curItem, setAttrDict):
    ## 设置生成装备所有属性
    for key, value in setAttrDict.items():
        key = GameWorld.ToIntDef(key, key)
        # 需支持
        # 1. UserData格式: {'19': ['1889', '2034', '893', '927'], '50': ['1702622046'], '17': ['39', '33', '34', '9']}
        # 2. 自定义字典: {"key":value, ...} key支持自定义或与UserData的key,value支持数值或列表,列表元素支持字符串或数值,均默认转为数值
        # 数值类型的默认为UserData属性
        if isinstance(key, int):
            if key % 2 == 0: # 偶数是单数值
                v = 0
                if isinstance(value, int):
                    v = value
                elif (isinstance(value, list) or isinstance(value, tuple)) and value:
                    v = GameWorld.ToIntDef(value[0], 0)
                curItem.SetUserAttr(key, v)
            elif isinstance(value, list) or isinstance(value, tuple): # 单数一定是要列表
                curItem.ClearUserAttr(key)
                for v in value:
                    v = GameWorld.ToIntDef(v, 0)
                    curItem.AddUserAttr(key, v)
        # 其他指定字符串类型属性
        else:
            GameWorld.Log("###AddCreateItemAttr unknown key:%s, value:%s, itemID=%s" % (key, value, curItem.GetItemTypeID()))
    MakeEquipGS(curItem)
    return
def GetCreateEquipAttr(curItem, curPlayer=None, setAttrDict=None):
    '''获取生成装备所有属性
    @param curPlayer: 可能为None
    @param setAttrDict: 直接设置物品的属性 {key:value, ...} key支持  ShareDefine.Def_IudetXXX字符串 或 自定key
    @return: None - 异常情况,物品实例需要clear
            equipAttrDict - 生成后的最新属性k:v字典,可直接用于 SetCreateEquipAttr
    '''
    if setAttrDict == None:
        setAttrDict = {}
    equipAttrDict = {}
    playerID = 0 if not curPlayer else curPlayer.GetPlayerID()
    itemID = curItem.GetItemTypeID()
    appointID = setAttrDict.get(ShareDefine.Def_CItemKey_AppointID)
    # 定制属性ID
    if appointID > 0:
        ipyData = IpyGameDataPY.GetIpyGameData("AppointItem", appointID)
        if not ipyData:
            return
        if ipyData.GetCancelUseLimit():
            equipAttrDict[str(ShareDefine.Def_IudetCancelUseLimit)] = 1
        equipAttrDict[str(ShareDefine.Def_IudetItemLV)] = ipyData.GetItemLV()
        equipAttrDict[str(ShareDefine.Def_IudetBaseAttrID)] = ipyData.GetBaseAttrID()
        equipAttrDict[str(ShareDefine.Def_IudetBaseAttrValue)] = ipyData.GetBaseAttrValue()
        equipAttrDict[str(ShareDefine.Def_IudetLegendAttrID)] = ipyData.GetLegendAttrID()
        equipAttrDict[str(ShareDefine.Def_IudetLegendAttrValue)] = ipyData.GetLegendAttrValue()
        GameWorld.DebugLog("    装备定制属性: itemID=%s,appointID=%s,equipAttrDict=%s,setAttrDict=%s" % (itemID, appointID, equipAttrDict, setAttrDict), playerID)
        return equipAttrDict
    # 以下为常规生成属性逻辑
    itemColor = curItem.GetItemColor()
    equipPlace = curItem.GetEquipPlace()
    if curPlayer:
        playerID, playerLV = curPlayer.GetPlayerID(), curPlayer.GetLV()
    else:
        playerLV = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_WorldAverageLv)
        GameWorld.DebugLog("生成装备时没有玩家等级, 取当前世界等级! itemID=%s,worldLV=%s" % (itemID, playerLV), playerID)
    colorIpyData = IpyGameDataPY.GetIpyGameData("EquipColor", itemColor)
    colorPlaceIpyData = IpyGameDataPY.GetIpyGameData("EquipColorPlace", itemColor, equipPlace)
    if not colorIpyData or not colorPlaceIpyData:
        GameWorld.ErrLog("生成装备时找不到对应配置! itemID=%s,itemColor=%s,equipPlace=%s" % (itemID, itemColor, equipPlace), playerID)
        return
    GameWorld.DebugLog("随机装备属性! itemID=%s,itemColor=%s,equipPlace=%s,setAttrDict=%s" % (itemID, itemColor, equipPlace, setAttrDict), playerID)
    itemLV = setAttrDict.get(str(ShareDefine.Def_IudetItemLV), 0)
    if not itemLV:
        # 随机等级
        lowLV, highLV = IpyGameDataPY.GetFuncEvalCfg("EquipRandRule", 1)
        randLVList = range(max(playerLV - lowLV, 1), playerLV + highLV)
        itemLV = random.choice(randLVList)
    equipAttrDict[str(ShareDefine.Def_IudetItemLV)] = itemLV
    reRatio = 0 # 参考基准值所在比率 0~1
    lvMin, lvMax = colorIpyData.GetRangeLV() # 品质对应的等级范围
    if itemLV <= lvMin:
        reRatio = 0
    elif itemLV >= lvMax:
        reRatio = 1
    else:
        reRatio = (itemLV - lvMin) / float(lvMax - lvMin)
    GameWorld.DebugLog("    itemLV=%s,lvMin=%s,lvMax=%s,reRatio=%s,playerLV=%s" % (itemLV, lvMin, lvMax, reRatio, playerLV), playerID)
    baseAttrIDList = setAttrDict.get(str(ShareDefine.Def_IudetBaseAttrID))
    baseAttrValueList = setAttrDict.get(str(ShareDefine.Def_IudetBaseAttrValue))
    # 这里注意None为未指定,支持当空列表[]时为不给该属性
    if baseAttrIDList != None and baseAttrValueList != None and len(baseAttrIDList) == len(baseAttrValueList):
        equipAttrDict[str(ShareDefine.Def_IudetBaseAttrID)] = baseAttrIDList
        equipAttrDict[str(ShareDefine.Def_IudetBaseAttrValue)] = baseAttrValueList
    else:
        __RandCreateEquipBaseAttr(reRatio, colorPlaceIpyData, equipAttrDict)
    legendAttrIDList = setAttrDict.get(str(ShareDefine.Def_IudetLegendAttrID))
    legendAttrValueList = setAttrDict.get(str(ShareDefine.Def_IudetLegendAttrValue))
    if legendAttrIDList != None and legendAttrValueList != None and len(legendAttrIDList) == len(legendAttrValueList):
        equipAttrDict[str(ShareDefine.Def_IudetLegendAttrID)] = legendAttrIDList
        equipAttrDict[str(ShareDefine.Def_IudetLegendAttrValue)] = legendAttrValueList
    else:
        __RandCreateEquipLegendAttr(reRatio, colorIpyData, equipAttrDict)
    # 神仙极属性可在这里扩展...
    GameWorld.DebugLog("    装备最终属性: equipAttrDict=%s,setAttrDict=%s" % (equipAttrDict, setAttrDict), playerID)
    return equipAttrDict
def __RandCreateEquipBaseAttr(reRatio, colorPlaceIpyData, setAttrDict):
    ## 随机装备基础属性
    baseAttrDict = {ShareDefine.Def_Effect_Atk:colorPlaceIpyData.GetRangeAtk(),
                    ShareDefine.Def_Effect_MaxHP:colorPlaceIpyData.GetRangeHP(),
                    ShareDefine.Def_Effect_Def:colorPlaceIpyData.GetRangeDef(),
                    ShareDefine.Def_Effect_AtkSpeed:colorPlaceIpyData.GetRangeAtkSpeed(),
                    }
    upDownRateList = IpyGameDataPY.GetFuncEvalCfg("EquipRandRule", 2)
    baseAttrIDList, baseAttrValueList = [], []
    for attrID in [ShareDefine.Def_Effect_Atk, ShareDefine.Def_Effect_MaxHP, ShareDefine.Def_Effect_Def, ShareDefine.Def_Effect_AtkSpeed]:
        valueList = baseAttrDict[attrID]
        atkValue = __GetRandAttrValueByRatio(valueList, reRatio, upDownRateList)
        if not atkValue:
            continue
        baseAttrIDList.append(attrID)
        baseAttrValueList.append(atkValue)
    if baseAttrIDList:
        setAttrDict[str(ShareDefine.Def_IudetBaseAttrID)] = baseAttrIDList
        setAttrDict[str(ShareDefine.Def_IudetBaseAttrValue)] = baseAttrValueList
    GameWorld.DebugLog("    baseAttrIDList=%s,baseAttrValueList=%s" % (baseAttrIDList, baseAttrValueList))
    return
def __RandCreateEquipLegendAttr(reRatio, colorIpyData, setAttrDict):
    ## 随机装备传奇属性
    upDownRateList = colorIpyData.GetRangeRand()
    battleAttrCount = colorIpyData.GetBattleAttrCount() # 战斗属性条数
    battleDefAttrCount = colorIpyData.GetBattleDefAttrCount() #战斗抗性条数
    if not battleAttrCount and not battleDefAttrCount:
        return
    # 注: 闪避率-对应原闪避值;抗闪避-对应原命中值
    battleAttrDict = {ShareDefine.Def_Effect_FaintRate:colorIpyData.GetRangeFaintRate(),
                      ShareDefine.Def_Effect_SuperHitRate:colorIpyData.GetRangeSuperHitRate(),
                      ShareDefine.Def_Effect_ComboRate:colorIpyData.GetRangeComboRate(),
                      ShareDefine.Def_Effect_Miss:colorIpyData.GetRangeMissRate(),
                      ShareDefine.Def_Effect_AtkBackRate:colorIpyData.GetRangeAtkBackRate(),
                      ShareDefine.Def_Effect_SuckHPPer:colorIpyData.GetRangeSuckHPPer(),
                      }
    battleDefAttrDict = {ShareDefine.Def_Effect_FaintDefRate:colorIpyData.GetRangeFaintDefRate(),
                         ShareDefine.Def_Effect_SuperHitRateReduce:colorIpyData.GetRangeSuperHitRateReduce(),
                         ShareDefine.Def_Effect_ComboDefRate:colorIpyData.GetRangeComboDefRate(),
                         ShareDefine.Def_Effect_Hit:colorIpyData.GetRangeMissDefRate(),
                         ShareDefine.Def_Effect_AtkBackDefRate:colorIpyData.GetRangeAtkBackDefRate(),
                         ShareDefine.Def_Effect_SuckHPDefPer:colorIpyData.GetRangeSuckHPDefPer(),
                         }
    # 没有配置值的属性不产出,打乱顺序后直接取限制条数前x个即为随机的属性ID
    # 战斗属性
    for k, v in battleAttrDict.items():
        if not v:
            battleAttrDict.pop(k)
    battleAttrIDList = battleAttrDict.keys()
    battleAttrIDList and random.shuffle(battleAttrIDList)
    battleAttrIDList = battleAttrIDList[:battleAttrCount]
    batAttrIDList, batAttrValueList= [], []
    for attrID in battleAttrIDList:
        atkValue = __GetRandAttrValueByRatio(battleAttrDict[attrID], reRatio, upDownRateList)
        if not atkValue:
            continue
        batAttrIDList.append(attrID)
        batAttrValueList.append(atkValue)
    GameWorld.DebugLog("    战斗属性条数=%s,IDList=%s,ValueList=%s" % (battleAttrCount, batAttrIDList, batAttrValueList))
    # 战斗抗性
    for k, v in battleDefAttrDict.items():
        if not v:
            battleDefAttrDict.pop(k)
    battleDefAttrIDList = battleDefAttrDict.keys()
    battleDefAttrIDList and random.shuffle(battleDefAttrIDList)
    battleDefAttrIDList = battleDefAttrIDList[:battleDefAttrCount]
    batDefAttrIDList, batDefAttrValueList= [], []
    for attrID in battleDefAttrIDList:
        atkValue = __GetRandAttrValueByRatio(battleDefAttrDict[attrID], reRatio, upDownRateList)
        if not atkValue:
            continue
        batDefAttrIDList.append(attrID)
        batDefAttrValueList.append(atkValue)
    GameWorld.DebugLog("    战斗抗性条数=%s,IDList=%s,ValueList=%s" % (battleDefAttrCount, batDefAttrIDList, batDefAttrValueList))
    # 全部放在传奇属性里
    legendAttrIDList = batAttrIDList + batDefAttrIDList
    legendAttrValueList = batAttrValueList + batDefAttrValueList
    if legendAttrIDList:
        setAttrDict[str(ShareDefine.Def_IudetLegendAttrID)] = legendAttrIDList
        setAttrDict[str(ShareDefine.Def_IudetLegendAttrValue)] = legendAttrValueList
    GameWorld.DebugLog("    legendAttrIDList=%s,legendAttrValueList=%s" % (legendAttrIDList, legendAttrValueList))
    return
def __GetRandAttrValueByRatio(valueList, reRatio, upDownRateList):
    ## 根据参考比例随机属性值
    valueMin, valueMax = valueList
    if reRatio < 0 or reRatio > 1:
        # 不合法的参考比例,默认最小值,合法值为0~1,包含0跟1
        return valueMin
    diffValue = valueMax - valueMin
    reValue = valueMin + int(diffValue * reRatio) # 参考值
    downRate, upRate = upDownRateList
    randValueMin = int(reValue - math.ceil(diffValue * downRate / 10000.0)) # 至少-1
    randValueMax = int(reValue + math.ceil(diffValue * upRate / 10000.0)) # 至少+1
    randValue = random.randint(randValueMin, randValueMax)
    GameWorld.DebugLog("        valueList=%s,reRatio=%s,upDownRateList=%s,diffValue=%s,reValue=%s,%s~%s,%s"
                       % (valueList, reRatio, upDownRateList, diffValue, reValue, randValueMin, randValueMax, randValue))
    return randValue
def UpdateItemUserData(curItem, updateDict={}, delKeyList=[], isUpdateGS=False):
    ''' 更新物品UserData数据
    @param curItem: IPY_SingleItem 或  IPY_RoleItem