hxp
2026-03-12 902447caa0c5c7b31dd3add5cb69053915f18fca
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHero.py
@@ -20,14 +20,22 @@
import IpyGameDataPY
import IPY_GameWorld
import ItemControler
import PlayerSuccess
import ChPyNetSendPack
import OpenServerActivity
import PlayerActivity
import NetPackCommon
import PlayerControl
import PlayerOnline
import PlayerPreset
import PlayerBeauty
import PlayerTask
import PlayerHJG
import GameWorld
import ChConfig
import random
import math
    
def PlayerOnDay(curPlayer):
    if curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroAwakeRebirthCnt):
@@ -38,6 +46,9 @@
def OnPlayerLogin(curPlayer):
    Sync_HeroInfo(curPlayer)
    Sync_PlayerHeroInfo(curPlayer)
    Sync_LineupRecommendInfo(curPlayer)
    Sync_HeroFatesInfo(curPlayer)
    __CheckOSAHeroStar(curPlayer)
    return
def OnPlayerFirstLogin(curPlayer):
@@ -87,10 +98,10 @@
        return
    GameWorld.DebugLog("初始化新手武将: %s" % defaultHeroInfo, curPlayer.GetPlayerID())
    
    lineupID = ShareDefine.Lineup_Main
    presetID = 1 # 默认预设1
    shapeType = 0
    for heroID, posNum in defaultHeroInfo.items():
        lineupValue = ComLineupValue(lineupID, shapeType, posNum)
        lineupValue = ComLineupValue(presetID, shapeType, posNum)
        setAttrDict = {ShareDefine.Def_IudetHeroLineup:[lineupValue]}
        ItemControler.GivePlayerItem(curPlayer, heroID, 1, False, [ShareDefine.rptHero], setAttrDict=setAttrDict)
        
@@ -107,6 +118,8 @@
        singleItem.SetUserAttr(ShareDefine.Def_IudetHeroAwakeLV, 0)
    if singleItem.GetUserAttr(ShareDefine.Def_IudetHeroSkin):
        singleItem.SetUserAttr(ShareDefine.Def_IudetHeroSkin, 0)
    if singleItem.GetUserAttr(ShareDefine.Def_IudetHeroSkinAttr):
        singleItem.SetUserAttr(ShareDefine.Def_IudetHeroSkinAttr, 0)
        
    if singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentID):
        singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroTalentID)
@@ -180,6 +193,8 @@
        GameWorld.DebugLog("首次激活武将: heroID=%s" % (heroID), curPlayer.GetPlayerID())
        #首次获得图鉴额外逻辑 ...
        Sync_HeroInfo(curPlayer, [heroID])
        PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 首次获得
        OpenServerActivity.UpdOSA_HeroTrainBillboard(curPlayer) # 首次获得
        
    return
@@ -221,29 +236,21 @@
    GameWorld.DebugLog("设置武将图鉴激活状态:%s,bookState=%s,updBookState=%s" % (isAct, bookState, updBookState), curPlayer.GetPlayerID())
    return
def GetHeroBookStarLV(curPlayer, heroID):
    ## 武将图鉴星级等级
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    return GameWorld.GetValue(bookState, 4, 3)
def SetHeroBookStarLV(curPlayer, heroID, starLV):
    ## 设置武将图鉴星级等级,支持三位数 0~999 级
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    updBookState = GameWorld.SetValue(bookState, 4, 3, starLV)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroBook % heroID, updBookState)
    GameWorld.DebugLog("设置武将图鉴星级等级:%s,bookState=%s,updBookState=%s" % (starLV, bookState, updBookState), curPlayer.GetPlayerID())
    return
def GetHeroBookBreakLV(curPlayer, heroID):
    ## 武将图鉴突破等级
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    return GameWorld.GetValue(bookState, 7, 3)
def SetHeroBookBreakLV(curPlayer, heroID, breakLV):
    ## 设置武将图鉴突破等级,支持三位数 0~999 级
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    updBookState = GameWorld.SetValue(bookState, 7, 3, breakLV)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroBook % heroID, updBookState)
    GameWorld.DebugLog("设置武将图鉴突破等级:%s,bookState=%s,updBookState=%s" % (breakLV, bookState, updBookState), curPlayer.GetPlayerID())
    return
## Def_PDict_HeroSkinInfo 星级*10+是否已激活
def GetHeroSkinState(curPlayer, skinID):
    ## 武将时装激活状态
    return curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroSkinInfo % skinID) % 10
def SetHeroSkinState(curPlayer, skinID, state):
    info = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroSkinInfo % skinID)
    info = info / 10 * 10 + min(1, state)
    return PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroSkinInfo % skinID, info)
def GetHeroSkinStar(curPlayer, skinID):
    ## 武将时装星级
    return curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroSkinInfo % skinID) / 10
def SetHeroSkinStar(curPlayer, skinID, star):
    info = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroSkinInfo % skinID)
    info = star * 10 + info % 10
    return PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroSkinInfo % skinID, info)
def GetHeroItem(curPlayer, itemIndex):
    curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
@@ -256,20 +263,16 @@
        return
    return heroItem
def GetHeroLineupPosNum(heroItem, lineupID=ShareDefine.Lineup_Main):
    ## 获取英雄所在阵型站位
    # @param lineupID: 阵型ID,默认主阵型
    # @return: 0-没有在该阵型;>0-在该阵型中的站位编号
    lineupCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroLineup)
    if not lineupCount:
        return 0
    for lpIndex in range(lineupCount)[::-1]:
        lineupValue = heroItem.GetUserAttrByIndex(ShareDefine.Def_IudetHeroLineup, lpIndex)
        lpID, _, posNum = GetLineupValue(lineupValue)
        if lpID != lineupID:
            continue
        return posNum
    return 0
def GetHeroEffPresetIDList(heroItem):
    ## 获取英雄有生效的预设ID列表
    dataCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroEffPresetID)
    if not dataCount:
        return []
    effPresetIDList = []
    for lpIndex in range(dataCount):
        presetID = heroItem.GetUserAttrByIndex(ShareDefine.Def_IudetHeroEffPresetID, lpIndex)
        effPresetIDList.append(presetID)
    return effPresetIDList
#// B2 30 武将升级 #tagCSHeroLVUP
#
@@ -292,12 +295,8 @@
    quality = heroIpyData.GetQuality()
    breakLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
    heroLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroLV)
    LVMax = GetHeroLVMax(heroItem)
    GameWorld.DebugLog("请求武将升级: itemIndex=%s,heroID=%s,heroLV=%s,quality=%s,breakLV=%s,LVMax=%s"
                       % (itemIndex, heroID, heroLV, quality, breakLV, LVMax), playerID)
    if heroLV >= LVMax:
        GameWorld.DebugLog("该武将已满级!LVMax=%s" % (LVMax), playerID)
        return
    GameWorld.DebugLog("请求武将升级: itemIndex=%s,heroID=%s,heroLV=%s,quality=%s,breakLV=%s"
                       % (itemIndex, heroID, heroLV, quality, breakLV), playerID)
    qualityLVIpyData = IpyGameDataPY.GetIpyGameData("HeroQualityLV", quality, heroLV)
    if not qualityLVIpyData:
        return
@@ -322,7 +321,12 @@
    GameWorld.DebugLog("武将升级: itemIndex=%s,heroID=%s,updHeroLV=%s" % (itemIndex, heroID, updHeroLV), playerID)
    heroItem.SetUserAttr(ShareDefine.Def_IudetHeroLV, updHeroLV)
    
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 升级
    PlayerActivity.AddDailyTaskValue(curPlayer, ChConfig.DailyTask_HeroLVUP, 1)
    PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_HeroLVUP)
    PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_OSAHeroLVUP, 1)
    OpenServerActivity.UpdOSA_HeroTrainBillboard(curPlayer) # 升级
    return
def GetHeroLVMax(heroItem):
@@ -332,12 +336,10 @@
    if not heroIpyData:
        return 0
    quality = heroIpyData.GetQuality()
    breakLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
    qualityBreakIpyData = IpyGameDataPY.GetIpyGameData("HeroQualityBreak", quality, breakLV)
    if not qualityBreakIpyData:
    lvIpyDataList = IpyGameDataPY.GetIpyGameDataByCondition("HeroQualityLV", {"Quality":quality}, True)
    if not lvIpyDataList:
        return 0
    LVMax = qualityBreakIpyData.GetLVMax()
    return LVMax
    return len(lvIpyDataList)
#// B2 31 武将升星 #tagCSHeroStarUP
#
@@ -374,10 +376,14 @@
    if awakeRandCnt:
        GameWorld.ErrLog("武将觉醒解锁天赋未选择,无法升星! itemIndex=%s,heroID=%s" % (itemIndex, heroID), playerID)
        return
    useBreakLV = useItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
    useAwakeLV = useItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
    if useBreakLV or useAwakeLV:
        GameWorld.DebugLog("材料卡突破或觉醒等级不为0暂时无法升星!useBreakLV=%s,useAwakeLV=%s" % (useBreakLV, useAwakeLV), playerID)
    if useAwakeLV:
        GameWorld.DebugLog("材料卡觉醒等级不为0暂时无法升星!useAwakeLV=%s" % (useAwakeLV), playerID)
        return
    useHeroLV = useItem.GetUserAttr(ShareDefine.Def_IudetHeroLV)
    useBreakLV = useItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
    if useHeroLV > 1 or useBreakLV:
        GameWorld.DebugLog("材料卡已升级或突破暂时无法升星!useHeroLV=%s,useBreakLV=%s" % (useHeroLV, useBreakLV), playerID)
        return
    
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
@@ -386,7 +392,7 @@
    quality = heroIpyData.GetQuality()
    star = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
    awakeLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
    starMax = GetHeroStarMax(heroItem)
    starMax = GetHeroStarMax(heroID, heroItem)
    GameWorld.DebugLog("heroID=%s,star=%s,quality=%s,awakeLV=%s,starMax=%s" % (heroID, star, quality, awakeLV, starMax), playerID)
    if star >= starMax:
        GameWorld.DebugLog("该武将已满星!starMax=%s" % (starMax), playerID)
@@ -398,11 +404,33 @@
                       % (itemIndex, heroID, star, useStar, addStar, updStar), playerID)
    ItemCommon.DelItem(curPlayer, useItem, useItem.GetCount(), False, "HeroStarUP")
    DoHeroUpdStar(curPlayer, heroItem, updStar)
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 升星
    # 固定返还一个遣散本体材料
    qualityIpyData = IpyGameDataPY.GetIpyGameData("HeroQuality", quality)
    if qualityIpyData:
        ratio = IpyGameDataPY.GetFuncCfg("HeroRebirth", 5)
        returnItemList = []
        dismissReturnItems = qualityIpyData.GetDismissReturnItems()
        for itemID, itemCount in dismissReturnItems:
            returnCnt = max(1, int(itemCount * ratio / 100.0))
            returnItemList.append([itemID, returnCnt])
        returnItemExDict = {}
        __calcHeroQualityReturnItemEx(curPlayer, qualityIpyData, returnItemExDict)
        for key, itemCount in returnItemExDict.items():
            itemID, isBind = key
            returnItemList.append([itemID, itemCount, isBind])
        GameWorld.DebugLog("吞噬额外总返还: %s" % returnItemList)
        ItemControler.GivePlayerItemOrMail(curPlayer, returnItemList, event=["HeroStarUPReturn", False, {}])
    OpenServerActivity.UpdOSA_HeroTrainBillboard(curPlayer) # 升星
    return
def GetHeroStarMax(heroItem):
    ## 获取武将当前最大星级
    heroID = heroItem.GetItemTypeID()
def GetHeroStarMax(heroID, heroItem=None):
    ## 获取武将卡物品当前最大星级
    # @param heroItem: 传入武将物品时,则取本物品当前觉醒对应的最大星级
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return 0
@@ -412,10 +440,15 @@
        return 0
    InitStarUpper = qualityIpyData.GetInitStarUpper()
    
    addStarUpper = 0
    heroAwakeIpyDataList = IpyGameDataPY.GetIpyGameDataList("HeroAwake", heroID)
    if heroAwakeIpyDataList:
    awakeLV = 99999
    if heroItem:
        awakeLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
    if awakeLV <= 0:
        return InitStarUpper
    addStarUpper = 0
    heroAwakeIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("HeroAwake", heroID)
    if heroAwakeIpyDataList:
        for ipyData in heroAwakeIpyDataList:
            if ipyData.GetAwakeLV() > awakeLV:
                break
@@ -427,28 +460,51 @@
def DoHeroUpdStar(curPlayer, heroItem, updStar, isSync=True):
    ## 执行武将星级更新
    heroID = heroItem.GetItemTypeID()
    versionStarMax = GetHeroStarMax(heroID) # 版本理论最大星级
    if updStar > versionStarMax:
        updStar = versionStarMax
        GameWorld.DebugLog("不超过版本最大武将星级: heroID=%s,versionStarMax=%s" % (heroID, versionStarMax))
    curStar = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
    addStar = updStar - curStar
    item = heroItem.GetItem()
    item.SetUserAttr(ShareDefine.Def_IudetHeroStar, updStar)
    if addStar > 0:
        __DoHeroStarTalentUp(item, addStar)
        PlayerActivity.AddDailyTaskValue(curPlayer, ChConfig.DailyTask_HeroStarUP, addStar)
        PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_HeroStarUP, addStar)
        PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_OSAHeroStarUP, addStar)
    if isSync:
        heroItem.Sync_Item()
    itemIndex = heroItem.GetItemPlaceIndex()
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    #starLVH = GetHeroBookStarLVH(curPlayer, heroID)
    #if updStar > starLVH:
    #    SetHeroBookStarLVH(curPlayer, heroID, updStar)
    return
def __DoHeroStarTalentUp(singleItem, addLV):
    ## 执行武将星级天赋等级提升
    
    heroID = singleItem.GetItemTypeID()
    commTalentSlot = IpyGameDataPY.GetFuncCfg("HeroStarTalent", 1) # 常规天赋槽个数
    talentMaxLV = IpyGameDataPY.GetFuncCfg("HeroStarTalent", 2) # 每个天赋最大等级
    maxUnlockSlot = commTalentSlot # 最大有效的已解锁槽位
    awakeIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("HeroAwake", heroID)
    if awakeIpyDataList:
        awakeLV = singleItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
        for ipyData in awakeIpyDataList[:awakeLV][::-1]: # 倒序遍历,第一个命中的就是最大的
            unlockTalentSlot = ipyData.GetUnlockTalentSlot()
            if unlockTalentSlot and unlockTalentSlot :
                maxUnlockSlot = unlockTalentSlot
                break
    idCount = singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentID)
    lvCount = singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentIDLV)
    idList, lvList = [], [] # 记录在物品上的值,有顺序
    unfullLVIDList = [] # 未满级的天赋ID
    unfullLVIDListUnlock = [] # 未满级的天赋ID,仅已解锁槽位,重生可能导致觉醒已解锁槽位暂时被锁住
    haveUp = False
    for index in range(min(idCount, lvCount)):
        talentID = singleItem.GetUserAttrByIndex(ShareDefine.Def_IudetHeroTalentID, index)
@@ -457,14 +513,16 @@
        lvList.append(talentLV)
        if talentLV < talentMaxLV:
            unfullLVIDList.append(talentID)
            if index < maxUnlockSlot:
                unfullLVIDListUnlock.append(talentID)
    if len(idList) < commTalentSlot:
        idList += [0] * (commTalentSlot - len(idList))
        lvList += [0] * (commTalentSlot - len(lvList))
        
    GameWorld.DebugLog("执行武将星级天赋等级提升: addLV=%s" % addLV)
    GameWorld.DebugLog("当前星级天赋: idList=%s,lvList=%s" % (idList, lvList))
    GameWorld.DebugLog("未满级星级天赋ID: %s" % unfullLVIDList)
    GameWorld.DebugLog("当前星级天赋: idList=%s,lvList=%s,maxUnlockSlot=%s" % (idList, lvList, maxUnlockSlot))
    GameWorld.DebugLog("未满级星级天赋ID: %s,unfullLVIDListUnlock=%s" % (unfullLVIDList, unfullLVIDListUnlock))
    
    # 有空余槽位,优先给空余槽位天赋,额外解锁的槽位是需要先选择的,所以一定不为空,故这里只判断常规槽位即可
    if 0 in idList:
@@ -496,6 +554,7 @@
            idList[zeroIndex] = randTalentID
            lvList[zeroIndex] = 1
            unfullLVIDList.append(randTalentID)
            unfullLVIDListUnlock.append(randTalentID)
            GameWorld.DebugLog("新增星级天赋ID: %s" % (randTalentID))
            addLV -= 1
            haveUp = True
@@ -505,9 +564,9 @@
        for _ in range(addLV):
            if not unfullLVIDList:
                break
            randID = random.choice(unfullLVIDList)
            # 优先随机已解锁的
            randID = random.choice(unfullLVIDListUnlock) if unfullLVIDListUnlock else random.choice(unfullLVIDList)
            if randID not in idList:
                unfullLVIDList.remove(randID)
                continue
            randIndex = idList.index(randID)
            idLV = lvList[randIndex]
@@ -518,8 +577,11 @@
                GameWorld.DebugLog("升级星级天赋ID: %s,idLV=%s,index=%s" % (randID, idLV, randIndex))
                
            if idLV >= talentMaxLV:
                unfullLVIDList.remove(randID)
                GameWorld.DebugLog("    移除未满级ID: %s,unfullLVIDList=%s" % (randID, unfullLVIDList))
                if randID in unfullLVIDList:
                    unfullLVIDList.remove(randID)
                if randID in unfullLVIDListUnlock:
                    unfullLVIDListUnlock.remove(randID)
                GameWorld.DebugLog("    移除满级ID: %s,unfullLVIDList=%s,unfullLVIDListUnlock=%s" % (randID, unfullLVIDList, unfullLVIDListUnlock))
                
            haveUp = True
            
@@ -533,6 +595,41 @@
            break
        singleItem.AddUserAttr(ShareDefine.Def_IudetHeroTalentID, talentID)
        singleItem.AddUserAttr(ShareDefine.Def_IudetHeroTalentIDLV, lvList[index])
    return
def GetHeroIDStar(curPlayer, heroID):
    ## 获取某个武将ID当前星级,同时存在多张卡时取最大的星级
    starMax = 0
    curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
    for index in range(curPack.GetCount()):
        heroItem = curPack.GetAt(index)
        if not heroItem or heroItem.IsEmpty():
            continue
        if heroID != heroItem.GetItemTypeID():
            continue
        starMax = max(heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar), starMax)
    return starMax
def GetHeroStarTotal(curPlayer):
    ## 武将总星级
    totalStar = 0
    curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
    for index in range(curPack.GetCount()):
        heroItem = curPack.GetAt(index)
        if not heroItem or heroItem.IsEmpty():
            continue
        totalStar += heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
    return totalStar
def __CheckOSAHeroStar(curPlayer):
    ## 线上bug临时处理
    osaHeroStar = PlayerSuccess.GetSuccValue(curPlayer, ShareDefine.SuccType_OSAHeroStarUP, [])
    starTotal = GetHeroStarTotal(curPlayer)
    addStar = starTotal - osaHeroStar
    if addStar <= 0:
        return
    GameWorld.Log("上线修正庆典武将升星成就进度! addStar=%s,starTotal=%s" % (addStar, starTotal))
    PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_OSAHeroStarUP, addStar)
    return
#// B2 32 武将突破 #tagCSHeroBreak
@@ -561,38 +658,42 @@
    ipyData = IpyGameDataPY.GetIpyGameData("HeroQualityBreak", quality, breakLV)
    if not ipyData:
        return
    LVMax = ipyData.GetLVMax()
    if heroLV < LVMax:
        GameWorld.DebugLog("未满级,无法突破: heroLV=%s < %s" % (heroLV, LVMax), playerID)
    UPLVNeed = ipyData.GetUPLVNeed()
    if heroLV < UPLVNeed:
        GameWorld.DebugLog("武将等级不足,无法突破: heroLV=%s < %s" % (heroLV, UPLVNeed), playerID)
        return
    nextBreakLV = breakLV + 1
    if not IpyGameDataPY.GetIpyGameData("HeroQualityBreak", quality, nextBreakLV):
        GameWorld.DebugLog("突破等级已满级: quality=%s,breakLV=%s" % (quality, breakLV), playerID)
        return
    costItemInfo = ipyData.GetUPCostItem()
    if not costItemInfo:
        return
    costItemID, costItemCount = costItemInfo
    if not costItemID or not costItemCount:
    costItemList = ipyData.GetUPCostItemList()
    if not costItemList:
        return
    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
    hasEnough, itemIndexList = ItemCommon.GetItem_FromPack_ByID(costItemID, itemPack, costItemCount)
    if not hasEnough:
        GameWorld.DebugLog("材料不足,武将无法突破! costItemID=%s, costItemCount=%s" % (costItemID, costItemCount))
    lackItemDict, delInfoDict = ItemCommon.GetCostItemIndexList(costItemList, itemPack)
    if lackItemDict:
        GameWorld.DebugLog("材料不足,武将无法突破! quality=%s,breakLV=%s,lackItemDict=%s" % (quality, breakLV, lackItemDict), playerID)
        return
    ItemCommon.ReduceItem(curPlayer, itemPack, itemIndexList, costItemCount, True, "HeroBreak")
    GameWorld.DebugLog("武将突破: itemIndex=%s,heroID=%s,nextBreakLV=%s" % (itemIndex, heroID, nextBreakLV), playerID)
    SetHeroBreakLV(heroItem, nextBreakLV)
    ItemCommon.DelCostItem(curPlayer, itemPack, delInfoDict, "HeroBreak")
    
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    GameWorld.DebugLog("武将突破: itemIndex=%s,heroID=%s,nextBreakLV=%s" % (itemIndex, heroID, nextBreakLV), playerID)
    SetHeroBreakLV(curPlayer, heroItem, nextBreakLV)
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 突破
    OpenServerActivity.UpdOSA_HeroTrainBillboard(curPlayer) # 突破
    return
def SetHeroBreakLV(heroItem, breakLV, isSync=True):
def SetHeroBreakLV(curPlayer, heroItem, breakLV, isSync=True):
    ## 设置武将突破等级
    #heroID = heroItem.GetItemTypeID()
    item = heroItem.GetItem()
    item.SetUserAttr(ShareDefine.Def_IudetHeroBreakLV, breakLV)
    if isSync:
        heroItem.Sync_Item()
    #breakLVH = GetHeroBookBreakLVH(curPlayer, heroID)
    #if breakLV > breakLVH:
    #    SetHeroBookBreakLVH(curPlayer, heroID, breakLV)
    return
#// B2 33 武将觉醒 #tagCSHeroAwake
@@ -645,7 +746,8 @@
    GameWorld.DebugLog("武将觉醒: itemIndex=%s,heroID=%s,nextAwakeLV=%s" % (itemIndex, heroID, nextAwakeLV), playerID)
    SetHeroAwakeLV(heroItem, nextAwakeLV)
    
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 觉醒
    OpenServerActivity.UpdOSA_HeroTrainBillboard(curPlayer) # 觉醒
    return
def SetHeroAwakeLV(heroItem, awakeLV, isSync=True):
@@ -661,7 +763,7 @@
    ## 觉醒解锁天赋槽
    heroID = singleItem.GetItemTypeID()
    awakeLV = singleItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
    awakeIpyDataList = IpyGameDataPY.GetIpyGameDataList("HeroAwake", heroID)
    awakeIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("HeroAwake", heroID)
    if not awakeIpyDataList:
        return
    maxUnlockSlot = 0
@@ -757,21 +859,51 @@
        idList.append(talentID)
        lvList.append(talentLV)
        
    commTalentSlot = IpyGameDataPY.GetFuncCfg("HeroStarTalent", 1) # 常规天赋槽个数
    if idCount < commTalentSlot:
        idList += [0] * (commTalentSlot - idCount)
        lvList += [0] * (commTalentSlot - idCount)
    if selectTalentID in idList:
        GameWorld.ErrLog("选择天赋ID不能重复! heroID=%s,selectIndex=%s,selectTalentID=%s in %s" 
                         % (heroID, selectIndex, selectTalentID, idList), playerID)
        return
    commTalentSlot = IpyGameDataPY.GetFuncCfg("HeroStarTalent", 1) # 常规天赋槽个数
    talentMaxLV = IpyGameDataPY.GetFuncCfg("HeroStarTalent", 2) # 每个天赋最大等级
    idCount = len(idList)
    GameWorld.DebugLog("选择天赋: heroID=%s,selectTalentID=%s,idList=%s,lvList=%s" % (heroID, selectTalentID, idList, lvList))
    
    if idCount < commTalentSlot:
        idList += [0] * (commTalentSlot - idCount)
        lvList += [0] * (commTalentSlot - idCount)
        selectTalentLV = 1
        GameWorld.DebugLog("常规槽位未全部解锁,觉醒槽位默认1级天赋")
    else:
        heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
        if not heroIpyData:
            return
        quality = heroIpyData.GetQuality()
        qualityIpyData = IpyGameDataPY.GetIpyGameData("HeroQuality", quality)
        if not qualityIpyData:
            return
        InitStarUpper = qualityIpyData.GetInitStarUpper()
        heroAwakeIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("HeroAwake", heroID)
        if not heroAwakeIpyDataList:
            return
        preSlotStarMax = 0 # 上一档槽位最大星级
        preSlotStarMax += InitStarUpper
        for ipyData in heroAwakeIpyDataList:
            if idCount < ipyData.GetUnlockTalentSlot():
                break
            preSlotStarMax += ipyData.GetAddStarUpper()
        curStar = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
        GameWorld.DebugLog("常规槽位已全部解锁, idCount=%s,preSlotStarMax=%s,curStar=%s" % (idCount, preSlotStarMax, curStar))
        overflowStar = curStar - preSlotStarMax # 溢出星级 = 当前星级 - 上一档槽位最大星级
        selectTalentLV = max(1, min(overflowStar + 1, talentMaxLV)) # 保底1级 + 溢出星级
    idList.append(selectTalentID)
    lvList.append(1)
    lvList.append(selectTalentLV)
    singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroTalentIDAwakeRand)
    
    GameWorld.DebugLog("更新选择天赋: selectTalentID=%s,idList=%s,lvList=%s" % (selectTalentID, idList, lvList))
    GameWorld.DebugLog("selectTalentID=%s,selectTalentLV=%s,idList=%s,lvList=%s" % (selectTalentID, selectTalentLV, idList, lvList))
    singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroTalentID)
    singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroTalentIDLV)
    for index, talentID in enumerate(idList):
@@ -783,7 +915,7 @@
    if isSync:
        heroItem.Sync_Item()
    
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 选择天赋
    return
#// B2 35 武将洗炼 #tagCSHeroWash
@@ -893,6 +1025,8 @@
    heroItem.Sync_Item()
    GameWorld.DebugLog("武将洗炼结果! itemIndex=%s,heroID=%s,washIDList=%s,lockTalentIndexs=%s" 
                       % (itemIndex, heroID, washIDList, lockTalentIndexs))
    OpenServerActivity.AddOSAValue(curPlayer, ShareDefine.Def_BT_OSA_HeroTrain, washCostItemCount)
    OpenServerActivity.UpdOSA_HeroTrainBillboard(curPlayer) # 洗炼
    return
def HeroTalentWashReplace(curPlayer, itemIndex, heroItem):
@@ -900,8 +1034,12 @@
    heroID = heroItem.GetItemTypeID()
    singleItem = heroItem.GetItem()
    
    washIDList = []
    idCount = singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentWashID)
    if not idCount:
        GameWorld.ErrLog("武将没有洗炼不需要替换! itemIndex=%s,heroID=%s" % (itemIndex, heroID))
        return
    washIDList = []
    singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroTalentID)
    for index in range(idCount):
        talentID = singleItem.GetUserAttrByIndex(ShareDefine.Def_IudetHeroTalentWashID, index)
@@ -913,21 +1051,47 @@
    heroItem.Sync_Item()
    GameWorld.DebugLog("武将洗炼替换! itemIndex=%s,heroID=%s,washIDList=%s" % (itemIndex, heroID, washIDList))
    
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 替换洗炼天赋
    return
#// B2 36 武将换肤 #tagCSHeroWearSkin
#// B2 36 武将皮肤操作 #tagCSHeroSkinOP
#
#struct    tagCSHeroWearSkin
#struct    tagCSHeroSkinOP
#{
#    tagHead        Head;
#    WORD        ItemIndex;    //武将物品所在武将背包位置索引
#    BYTE        SkinIndex;    //皮肤索引
#    DWORD        HeroID;        //武将ID
#    DWORD        SkinID;        //ʱװID
#    BYTE        OPType;        //操作 1-激活;2-选择形象;3-升星;4-选择属性
#    WORD        ItemIndex;    //武将物品所在武将背包位置索引,仅佩戴时有效
#};
def OnHeroWearSkin(index, clientData, tick):
def OnHeroSkinOP(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    heroID = clientData.HeroID
    skinID = clientData.SkinID
    opType = clientData.OPType
    itemIndex = clientData.ItemIndex
    skinIndex = clientData.SkinIndex
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return
    skinIDList = heroIpyData.GetSkinIDList()
    if skinID not in skinIDList:
        GameWorld.DebugLog("不存在该皮肤! heroID=%s,skinID=%s not in %s" % (heroID, skinID, skinIDList))
        return
    skinIndex = skinIDList.index(skinID)
    if opType == 1:
        ActiveHeroSkin(curPlayer, heroID, skinID)
    elif opType == 2:
        DoHeroWearSkin(curPlayer, itemIndex, skinIndex, False)
    elif opType == 3:
        DoHeroSkinStarUP(curPlayer, heroID, skinID)
    elif opType == 4:
        DoHeroWearSkin(curPlayer, itemIndex, skinIndex, True)
    return
def DoHeroWearSkin(curPlayer, itemIndex, skinIndex, isSkinAttr):
    # @param isSkinAttr: 是否是选择属性的,反之则为选择皮肤形象
    heroItem = GetHeroItem(curPlayer, itemIndex)
    if not heroItem:
        return
@@ -940,29 +1104,89 @@
        if skinIndex >= len(skinIDList):
            GameWorld.DebugLog("该武将不存在该皮肤! heroID=%s,skinIndex=%s" % (heroID, skinIndex))
            return
        skinState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroSkin % heroID)
        if not skinState & pow(2, skinIndex):
            GameWorld.DebugLog("该武将皮肤未解锁! heroID=%s,skinIndex=%s,skinState=%s" % (heroID, skinIndex, skinState))
        skinID = skinIDList[skinIndex]
        if not GetHeroSkinState(curPlayer, skinID):
            GameWorld.DebugLog("该武将皮肤未解锁! heroID=%s,skinIndex=%s" % (heroID, skinIndex))
            return
    heroItem.SetUserAttr(ShareDefine.Def_IudetHeroSkin, skinIndex)
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    GameWorld.DebugLog("切换武将皮肤! heroID=%s,skinIndex=%s,isSkinAttr=%s" % (heroID, skinIndex, isSkinAttr))
    item = heroItem.GetItem()
    if not isSkinAttr:
        item.SetUserAttr(ShareDefine.Def_IudetHeroSkin, skinIndex)
    else:
        # 选属性同步修改形象
        item.SetUserAttr(ShareDefine.Def_IudetHeroSkin, skinIndex)
        item.SetUserAttr(ShareDefine.Def_IudetHeroSkinAttr, skinIndex)
        PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 切换皮肤属性
    heroItem.Sync_Item()
    return True
def GMSetHeroSkin(curPlayer, heroID, skinIndex, isActive=1):
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return
    skinIDList = heroIpyData.GetSkinIDList()
    if not skinIDList or len(skinIDList) >= skinIndex:
        return
    skinID = skinIDList[skinIndex]
    __onHeroSkinActive(curPlayer, heroID, skinID, isActive)
    return
def ActiveHeroSkin(curPlayer, heroID, skinIndex, isActive=True):
    skinState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroSkin % heroID)
    if isActive:
        updState = skinState | pow(2, skinIndex)
        GameWorld.DebugLog("激活武将皮肤: heroID=%s,skinIndex=%s,skinState=%s,updState=%s"
                           % (heroID, skinIndex, skinState, updState), curPlayer.GetPlayerID())
    else:
        updState = GameWorld.SetBitValue(skinState, skinIndex, 0)
        GameWorld.DebugLog("失效武将皮肤: heroID=%s,skinIndex=%s,skinState=%s,updState=%s"
                           % (heroID, skinIndex, skinState, updState), curPlayer.GetPlayerID())
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroSkin % heroID, updState)
def ActiveHeroSkin(curPlayer, heroID, skinID):
    skinIpyData = IpyGameDataPY.GetIpyGameData("HeroSkinAttr", skinID)
    if not skinIpyData:
        return
    if GetHeroSkinState(curPlayer, skinID):
        GameWorld.DebugLog("该武将皮肤已经激活了: heroID=%s,skinID=%s" % (heroID, skinID))
        return
    needItemID = skinIpyData.GetNeedItemID()
    needItemCnt = 1
    costItemIndexList, bindCnt, unBindCnt = ItemCommon.GetPackItemBindStateIndexInfo(curPlayer, needItemID, needItemCnt)
    lackCnt = needItemCnt - bindCnt - unBindCnt
    if lackCnt > 0:
        GameWorld.DebugLog("激活武将时装物品不足! heroID=%s,needItemID=%s,needItemCnt=%s,lackCnt=%s" % (heroID, needItemID, needItemCnt, lackCnt))
        return
    ItemCommon.DelCostItemByBind(curPlayer, costItemIndexList, bindCnt, unBindCnt, needItemCnt, "HeroSkin")
    GameWorld.DebugLog("激活武将皮肤: heroID=%s,skinID=%s" % (heroID, skinID), curPlayer.GetPlayerID())
    __onHeroSkinActive(curPlayer, heroID, skinID, 1)
    return
def __onHeroSkinActive(curPlayer, heroID, skinID, isActive):
    SetHeroSkinState(curPlayer, skinID, isActive)
    Sync_HeroInfo(curPlayer, [heroID])
    RefreshLordAttr(curPlayer) # 时装激活 - 全体属性
    return
def DoHeroSkinStarUP(curPlayer, heroID, skinID):
    playerID = curPlayer.GetPlayerID()
    if not GetHeroSkinState(curPlayer, skinID):
        GameWorld.DebugLog("该武将时装未激活! heroID=%s,skinID=%s" % (heroID, skinID), playerID)
        return
    skinIpyData = IpyGameDataPY.GetIpyGameData("HeroSkinAttr", skinID)
    if not skinIpyData:
        return
    starMax = skinIpyData.GetStarMax()
    curStar = GetHeroSkinStar(curPlayer, skinID)
    if curStar >= starMax:
        GameWorld.DebugLog("武将时装星级已满! heroID=%s,skinID=%s,curStar=%s >= %s" % (heroID, skinID, curStar, starMax), playerID)
        return
    needItemID = skinIpyData.GetNeedItemID()
    needItemCnt = 1
    if not needItemID or not needItemCnt:
        return
    
    RefreshLordAttr(curPlayer)
    costItemIndexList, bindCnt, unBindCnt = ItemCommon.GetPackItemBindStateIndexInfo(curPlayer, needItemID, needItemCnt)
    lackCnt = needItemCnt - bindCnt - unBindCnt
    if lackCnt > 0:
        GameWorld.DebugLog("武将时装升星物品不足! heroID=%s,skinID=%s,needItemID=%s,needItemCnt=%s,lackCnt=%s" % (heroID, skinID, needItemID, needItemCnt, lackCnt))
        return
    ItemCommon.DelCostItemByBind(curPlayer, costItemIndexList, bindCnt, unBindCnt, needItemCnt, "Hero")
    nextStar = curStar + 1
    GameWorld.DebugLog("武将时装升星! heroID=%s,skinID=%s,nextStar=%s" % (heroID, skinID, nextStar), playerID)
    SetHeroSkinStar(curPlayer, skinID, nextStar)
    Sync_HeroInfo(curPlayer, [heroID])
    RefreshLordAttr(curPlayer) # 时装升星 - 全体属性
    return
#// B2 37 武将图鉴激活升级 #tagCSHeroBookUP
@@ -977,16 +1201,29 @@
def OnHeroBookUP(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    heroID = clientData.HeroID
    itemIndex = clientData.ItemIndex
    bookType = clientData.BookType
    
    if bookType == 1:
        __doHeroBookStarLVUP(curPlayer, heroID, itemIndex)
        pass #__doHeroBookStarLVUP(curPlayer, heroID)
    elif bookType == 2:
        __doHeroBookBreakLVUP(curPlayer, heroID, itemIndex)
        pass #__doHeroBookBreakLVUP(curPlayer, heroID)
    else:
        __doHeroBookAct(curPlayer, heroID)
    return
def GetHeroBookActCnt(curPlayer):
    ## 获取武将图鉴已激活数量
    bookCnt = 0
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for index in range(ipyDataMgr.GetHeroCount()):
        ipyData = ipyDataMgr.GetHeroByIndex(index)
        heroID = ipyData.GetHeroID()
        if not ipyData.GetPlayerCanUse():
            continue
        if not GetHeroBookInitState(curPlayer, heroID):
            continue
        bookCnt += 1
    return bookCnt
def __doHeroBookAct(curPlayer, heroID):
    ## 图鉴激活
@@ -1011,59 +1248,13 @@
    if awardMoneyInfo and len(awardMoneyInfo) == 2:
        moneyType, moneyValue = awardMoneyInfo
        if moneyType and moneyValue:
            PlayerControl.GiveMoney(curPlayer, moneyType, moneyValue, "HeroBookAct")
            PlayerControl.GiveMoney(curPlayer, moneyType, moneyValue, "HeroBookAct", notifyAward=True)
                    
    Sync_HeroInfo(curPlayer, [heroID])
    
    RefreshLordAttr(curPlayer)
    return
def __doHeroBookStarLVUP(curPlayer, heroID, itemIndex):
    ## 图鉴星级升级
    playerID = curPlayer.GetPlayerID()
    if not GetHeroBookInitState(curPlayer, heroID):
        GameWorld.DebugLog("该武将图鉴未激活! heroID=%s" % heroID, playerID)
        return
    heroItem = GetHeroItem(curPlayer, itemIndex)
    if not heroItem:
        return
    if heroItem.GetItemTypeID() != heroID:
        GameWorld.DebugLog("非该武将图鉴关联物品! heroID=%s,itemID=%s" % (heroID, heroItem.GetItemTypeID()), playerID)
        return
    heroStar = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
    bookStar = GetHeroBookStarLV(curPlayer, heroID)
    if bookStar >= heroStar:
        GameWorld.DebugLog("该武将图鉴星级已达当前英雄星级! heroID=%s,bookStar=%s,heroStar=%s" % (heroID, bookStar, heroStar), playerID)
        return
    GameWorld.DebugLog("武将图鉴星级升级! heroID=%s,bookStar=%s,heroStar=%s" % (heroID, bookStar, heroStar), playerID)
    SetHeroBookStarLV(curPlayer, heroID, bookStar + 1)
    Sync_HeroInfo(curPlayer, [heroID])
    RefreshLordAttr(curPlayer)
    return
def __doHeroBookBreakLVUP(curPlayer, heroID, itemIndex):
    ## 图鉴突破升级
    playerID = curPlayer.GetPlayerID()
    if not GetHeroBookInitState(curPlayer, heroID):
        GameWorld.DebugLog("该武将图鉴未激活! heroID=%s" % heroID, playerID)
        return
    heroItem = GetHeroItem(curPlayer, itemIndex)
    if not heroItem:
        return
    if heroItem.GetItemTypeID() != heroID:
        GameWorld.DebugLog("非该武将图鉴关联物品! heroID=%s,itemID=%s" % (heroID, heroItem.GetItemTypeID()), playerID)
        return
    heroBreakLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
    bookBreakLV = GetHeroBookBreakLV(curPlayer, heroID)
    if bookBreakLV >= heroBreakLV:
        GameWorld.DebugLog("该武将图鉴突破等级已达当前英雄突破等级! heroID=%s,bookBreakLV=%s,heroBreakLV=%s" % (heroID, bookBreakLV, heroBreakLV), playerID)
        return
    GameWorld.DebugLog("武将图鉴突破升级! heroID=%s,bookBreakLV=%s,heroBreakLV=%s" % (heroID, bookBreakLV, heroBreakLV), playerID)
    SetHeroBookBreakLV(curPlayer, heroID, bookBreakLV + 1)
    Sync_HeroInfo(curPlayer, [heroID])
    RefreshLordAttr(curPlayer)
    bookCnt = GetHeroBookActCnt(curPlayer)
    PlayerTask.UpdTaskValue(curPlayer, ChConfig.TaskType_HeroBook)
    PlayerSuccess.UptateSuccessProgress(curPlayer, ShareDefine.SuccType_OSAHeroBook, bookCnt)
    return
#// B2 38 武将锁定 #tagCSHeroLock
@@ -1090,10 +1281,16 @@
#{
#    tagHead        Head;
#    WORD        ItemIndex;    //武将物品所在武将背包位置索引
#    BYTE        LVReset;        //重置等级
#    BYTE        BreakReset;    //重置突破
#    BYTE        AwakeReset;    //重置觉醒
#};
def OnHeroRebirth(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    itemIndex = clientData.ItemIndex
    lvReset =  clientData.LVReset
    breakReset = clientData.BreakReset or lvReset # 突破受等级限制,所以等级重置突破必重置
    awakeReset = clientData.AwakeReset
    heroItem = GetHeroItem(curPlayer, itemIndex)
    if not heroItem:
        return
@@ -1104,7 +1301,7 @@
        GameWorld.DebugLog("该武将未进行过等级突破觉醒培养,不需要重生! itemIndex=%s" % (itemIndex))
        return
    
    if awakeLV:
    if awakeReset and awakeLV:
        rebirthCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroAwakeRebirthCnt)
        rebirthCntMax = IpyGameDataPY.GetFuncCfg("HeroRebirth", 2)
        if rebirthCntMax and rebirthCnt >= rebirthCntMax:
@@ -1116,45 +1313,63 @@
    if not heroIpyData:
        return
    quality = heroIpyData.GetQuality()
    ipyData = IpyGameDataPY.GetIpyGameData("HeroQualityAwake", quality, awakeLV)
    if not ipyData:
        return
    costMoney = ipyData.GetRebirthCostMoney()
    lvCostMoney = 0
    breakCostMoney = 0
    awakeCostMoney = 0
    moneyType = IpyGameDataPY.GetFuncCfg("HeroRebirth", 1)
    if moneyType and costMoney and not PlayerControl.HaveMoney(curPlayer, moneyType, costMoney):
    if awakeReset and awakeLV:
        qualityAwakeIpyData = IpyGameDataPY.GetIpyGameDataNotLog("HeroQualityAwake", quality, awakeLV)
        awakeCostMoney = qualityAwakeIpyData.GetRebirthCostMoney() if qualityAwakeIpyData else 0
    if lvReset:
        lvCostMoney = int(max(0, eval(IpyGameDataPY.GetFuncCompileCfg("HeroRebirth", 3))))
    if breakReset:
        breakCostMoney = int(max(0, eval(IpyGameDataPY.GetFuncCompileCfg("HeroRebirth2", 1))))
    costMoneyTotal = lvCostMoney + awakeCostMoney + breakCostMoney
    GameWorld.DebugLog("武将重生: itemIndex=%s,heroID=%s,quality=%s,heroLV=%s,breakLV=%s,awakeLV=%s,costMoneyTotal=%s(lv:%s+b:%s+a:%s),lvReset=%s,breakReset=%s,awakeReset=%s"
                       % (itemIndex, heroID, quality, heroLV, breakLV, awakeLV, costMoneyTotal, lvCostMoney, breakCostMoney, awakeCostMoney, lvReset, breakReset, awakeReset))
    if moneyType and costMoneyTotal and not PlayerControl.HaveMoney(curPlayer, moneyType, costMoneyTotal):
        return
    
    # 验证通过,可以重生
    GameWorld.DebugLog("武将重生: itemIndex=%s,heroID=%s,quality=%s,heroLV=%s,breakLV=%s,awakeLV=%s"
                       % (itemIndex, heroID, quality, heroLV, breakLV, awakeLV))
    ratio = IpyGameDataPY.GetFuncCfg("HeroRebirth", 4)
    returnItemDict = {}
    __calcHeroLVReturnitem(quality, heroLV, returnItemDict)
    __calcHeroBreakReturnitem(quality, breakLV, returnItemDict)
    __calcHeroAwakeReturnitem(quality, awakeLV, returnItemDict)
    if moneyType and costMoney and not PlayerControl.PayMoney(curPlayer, moneyType, costMoney, "HeroRebirth"):
    if lvReset:
        __calcHeroLVReturnitem(quality, heroLV, returnItemDict, ratio)
    if breakReset:
        __calcHeroBreakReturnitem(quality, breakLV, returnItemDict, ratio)
    if awakeReset:
        __calcHeroAwakeReturnitem(quality, awakeLV, returnItemDict, ratio)
    if moneyType and costMoneyTotal and not PlayerControl.PayMoney(curPlayer, moneyType, costMoneyTotal, "HeroRebirth"):
        return
    
    # 执行重生
    item = heroItem.GetItem()
    item.SetUserAttr(ShareDefine.Def_IudetHeroLV, 1)
    item.SetUserAttr(ShareDefine.Def_IudetHeroBreakLV, 0)
    item.SetUserAttr(ShareDefine.Def_IudetHeroAwakeLV, 0)
    if lvReset:
        item.SetUserAttr(ShareDefine.Def_IudetHeroLV, 1)
    if breakReset:
        item.SetUserAttr(ShareDefine.Def_IudetHeroBreakLV, 0)
    if awakeReset:
        item.SetUserAttr(ShareDefine.Def_IudetHeroAwakeLV, 0)
    heroItem.Sync_Item()
    
    if returnItemDict:
        returnItemList = [[k, v] for k, v in returnItemDict.items()]
        ItemControler.GivePlayerItemOrMail(curPlayer, returnItemList, event=["HeroRebirth", False, {}])
        
    if awakeLV:
    if awakeReset and awakeLV:
        rebirthCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroAwakeRebirthCnt)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroAwakeRebirthCnt, rebirthCnt + 1)
        Sync_PlayerHeroInfo(curPlayer)
        
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 重生
    return
def __calcHeroLVReturnitem(quality, heroLV, returnItemDict):
def __calcHeroLVReturnitem(quality, heroLV, returnItemDict, ratio):
    ## 计算武将等级返还
    returnDict = {}
    for retLV in range(1, heroLV):
@@ -1165,28 +1380,30 @@
        if not costItemInfo:
            continue
        costItemID, costItemCount = costItemInfo
        costItemCount = max(1, int(costItemCount * ratio / 100.0))
        returnItemDict[costItemID] = returnItemDict.get(costItemID, 0) + costItemCount
        returnDict[costItemID] = returnDict.get(costItemID, 0) + costItemCount
    GameWorld.DebugLog("    等级返还: quality=%s,heroLV=%s,%s,总%s" % (quality, heroLV, returnDict, returnItemDict))
    GameWorld.DebugLog("    等级返还: quality=%s,heroLV=%s,ratio=%s,%s,总%s" % (quality, heroLV, ratio, returnDict, returnItemDict))
    return
def __calcHeroBreakReturnitem(quality, breakLV, returnItemDict):
def __calcHeroBreakReturnitem(quality, breakLV, returnItemDict, ratio):
    # 计算武将突破返还
    returnDict = {}
    for retBreakLV in range(0, breakLV):
        qualityBreakIpyData = IpyGameDataPY.GetIpyGameData("HeroQualityBreak", quality, retBreakLV)
        if not qualityBreakIpyData:
            continue
        costItemInfo = qualityBreakIpyData.GetUPCostItem()
        if not costItemInfo:
        costItemList = qualityBreakIpyData.GetUPCostItemList()
        if not costItemList:
            continue
        costItemID, costItemCount = costItemInfo
        returnItemDict[costItemID] = returnItemDict.get(costItemID, 0) + costItemCount
        returnDict[costItemID] = returnDict.get(costItemID, 0) + costItemCount
    GameWorld.DebugLog("    突破返还: quality=%s,breakLV=%s,%s,总%s" % (quality, breakLV, returnDict, returnItemDict))
        for costItemID, costItemCount in costItemList:
            costItemCount = max(1, int(costItemCount * ratio / 100.0))
            returnItemDict[costItemID] = returnItemDict.get(costItemID, 0) + costItemCount
            returnDict[costItemID] = returnDict.get(costItemID, 0) + costItemCount
    GameWorld.DebugLog("    突破返还: quality=%s,breakLV=%s,ratio=%s,%s,总%s" % (quality, breakLV, ratio, returnDict, returnItemDict))
    return
def __calcHeroAwakeReturnitem(quality, awakeLV, returnItemDict):
def __calcHeroAwakeReturnitem(quality, awakeLV, returnItemDict, ratio):
    # 计算武将觉醒返还
    returnDict = {}
    for retAwakeLV in range(0, awakeLV):
@@ -1197,9 +1414,10 @@
        if not costItemInfo:
            continue
        costItemID, costItemCount = costItemInfo
        costItemCount = max(1, int(costItemCount * ratio / 100.0))
        returnItemDict[costItemID] = returnItemDict.get(costItemID, 0) + costItemCount
        returnDict[costItemID] = returnDict.get(costItemID, 0) + costItemCount
    GameWorld.DebugLog("    觉醒返还: quality=%s,awakeLV=%s,%s,总%s" % (quality, awakeLV, returnDict, returnItemDict))
    GameWorld.DebugLog("    觉醒返还: quality=%s,awakeLV=%s,ratio=%s,%s,总%s" % (quality, awakeLV, ratio, returnDict, returnItemDict))
    return
#// B2 40 武将遣散 #tagCSHeroDismiss
@@ -1215,8 +1433,10 @@
    itemIndexList = clientData.ItemIndexList
    GameWorld.DebugLog("武将遣散: itemIndexList=%s" % itemIndexList)
    
    ratio = IpyGameDataPY.GetFuncCfg("HeroRebirth", 5)
    dismissItemList = []
    returnItemDict = {}
    returnItemExDict = {}
    curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
    for itemIndex in itemIndexList:
        if itemIndex < 0 or itemIndex >= curPack.GetCount():
@@ -1237,6 +1457,10 @@
            GameWorld.DebugLog("上阵中的武将无法遣散! itemIndex=%s,lineupValueList=%s" % (itemIndex, lineupValueList))
            continue
        heroID = heroItem.GetItemTypeID()
        effPresetIDList = GetHeroEffPresetIDList(heroItem)
        if effPresetIDList:
            GameWorld.DebugLog("生效中的卡牌无法遣散! itemIndex=%s,heroID=%s,effPresetIDList=%s" % (itemIndex, heroID, effPresetIDList))
            continue
        heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
        if not heroIpyData:
            continue
@@ -1250,11 +1474,12 @@
        GameWorld.DebugLog("Dzɢ: itemIndex=%s,heroID=%s,quality=%s,heroLV=%s,breakLV=%s,heroStar=%s" % (itemIndex, heroID, quality, heroLV, breakLV, heroStar))
        dismissReturnItems = qualityIpyData.GetDismissReturnItems()
        for itemID, itemCount in dismissReturnItems:
            starRetCnt = (heroStar + 1) * itemCount
            starRetCnt = max(1, int((heroStar + 1) * itemCount * ratio / 100.0))
            returnItemDict[itemID] = returnItemDict.get(itemID, 0) + starRetCnt
        GameWorld.DebugLog("    星级返还: quality=%s,heroStar=%s,%s,总%s" % (quality, heroStar, dismissReturnItems, returnItemDict))
        __calcHeroLVReturnitem(quality, heroLV, returnItemDict)
        __calcHeroBreakReturnitem(quality, breakLV, returnItemDict)
        GameWorld.DebugLog("    星级返还: quality=%s,heroStar=%s,ratio=%s,%s,总%s" % (quality, heroStar, ratio, dismissReturnItems, returnItemDict))
        __calcHeroLVReturnitem(quality, heroLV, returnItemDict, ratio)
        __calcHeroBreakReturnitem(quality, breakLV, returnItemDict, ratio)
        __calcHeroQualityReturnItemEx(curPlayer, qualityIpyData, returnItemExDict)
        dismissItemList.append([itemIndex, heroItem])
    
    if not dismissItemList:
@@ -1265,48 +1490,91 @@
        
    if returnItemDict:
        returnItemList = [[k, v] for k, v in returnItemDict.items()]
        for key, itemCount in returnItemExDict.items():
            itemID, isBind = key
            returnItemList.append([itemID, itemCount, isBind])
        GameWorld.DebugLog("遣散总返还: %s" % returnItemList)
        ItemControler.GivePlayerItemOrMail(curPlayer, returnItemList, event=["HeroDismiss", False, {}])
        
    return
#// B4 12 战斗阵容保存 #tagCSHeroLineupSave
def __calcHeroQualityReturnItemEx(curPlayer, qualityIpyData, returnItemExDict):
    ## 其他功能额外返还
    # 红颜特殊效果: 遣散/吞噬额外返还百分比
    beautyReturnItems = qualityIpyData.GetBeautyReturnItems()
    if beautyReturnItems:
        isBind = ItemControler.GetIsBindValue(srcSign=ChConfig.ItemSrcSign_BeautyEff)
        retPer = PlayerBeauty.GetBeautyEffInfo(curPlayer, PlayerBeauty.EffType_HeroItemExPer)[0]
        for itemID, itemCount in beautyReturnItems:
            if not retPer:
                break
            key = (itemID, isBind)
            retCnt = max(1, int(math.ceil(itemCount * retPer / 100.0)))
            returnItemExDict[key] = returnItemExDict.get(key, 0) + retCnt
        GameWorld.DebugLog("    红颜返还: retPer=%s,%s,总%s" % (retPer, beautyReturnItems, returnItemExDict))
    # 称号特殊效果: 遣散/吞噬额外返还百分比
    titleReturnItems = qualityIpyData.GetTitleReturnItems()
    if titleReturnItems:
        isBind = ItemControler.GetIsBindValue(srcSign=ChConfig.ItemSrcSign_TitleEff)
        retPer = PlayerHJG.GetTitleEffInfo(curPlayer, PlayerHJG.TitleEff_HeroItemExPer)[0]
        for itemID, itemCount in titleReturnItems:
            if not retPer:
                break
            key = (itemID, isBind)
            retCnt = max(1, int(math.ceil(itemCount * retPer / 100.0)))
            returnItemExDict[key] = returnItemExDict.get(key, 0) + retCnt
        GameWorld.DebugLog("    称号返还: retPer=%s,%s,总%s" % (retPer, titleReturnItems, returnItemExDict))
    return
#// B4 12 战斗阵容预设保存 #tagCSHeroPresetSave
#
#struct    tagCSHeroLineupPos
#struct    tagCSHeroPresetPos
#{
#    WORD        ItemIndex;    //武将物品所在武将背包位置索引
#    BYTE        PosNum;        //1~n上阵位置编号  
#};
#
#struct    tagCSHeroLineupSave
#struct    tagCSHeroPresetSave
#{
#    tagHead        Head;
#    BYTE        LineupID;        //阵容ID:1-主阵容;其他待扩展,如某个防守阵容
#    BYTE        ShapeType;    //本阵容阵型,0为默认阵型,可扩展不同的阵型
#    BYTE        PresetID;        //阵容方案预设ID
#    BYTE        PosCnt;
#    tagCSHeroLineupPos    HeroPosList[PosCnt];    // 保存的阵容,只发送最终的阵容武将位置即可
#    tagCSHeroPresetPos    HeroPosList[PosCnt];    // 保存的阵容,只发送最终的阵容武将位置即可
#};
def OnHeroLineupSave(index, clientData, tick):
def OnHeroPresetSave(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    lineupID = clientData.LineupID
    shapeType = clientData.ShapeType
    presetID = clientData.PresetID
    shapeType = 0#clientData.ShapeType
    heroPosList = clientData.HeroPosList
    
    heroPosDict = {}
    indexList = []
    itemIndexPosDict = {}
    for posInfo in heroPosList:
        posNum = posInfo.PosNum
        itemIndex = posInfo.ItemIndex
        itemIndexPosDict[itemIndex] = posNum
    DoSaveHeroPreset(curPlayer, presetID, itemIndexPosDict, shapeType)
    return
def DoSaveHeroPreset(curPlayer, presetID, itemIndexPosDict, shapeType=0):
    if not PlayerPreset.GetFuncPresetIDState(curPlayer, presetID, ShareDefine.FuncPreset_Hero):
        GameWorld.DebugLog("该武将阵容预设不可用! presetID=%s" % presetID)
        return
    heroPosDict = {}
    indexList = []
    for itemIndex, posNum in itemIndexPosDict.items():
        if itemIndex in indexList:
            # 单武将只能一个位置,一个位置只能对应唯一武将单位
            continue
        indexList.append(itemIndex)
        heroPosDict[posNum] = itemIndex
        
    if lineupID not in ShareDefine.LineupList:
        GameWorld.DebugLog("不存在该阵容,无法保存! lineupID=%s" % lineupID)
        return
    GameWorld.DebugLog("保存阵容: lineupID=%s, %s" % (lineupID, heroPosDict), curPlayer.GetPlayerID())
    GameWorld.DebugLog("保存武将预设阵容: presetID=%s, %s" % (presetID, heroPosDict), curPlayer.GetPlayerID())
    curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
    # 直接重置旧阵型
    delCount = 0
@@ -1321,7 +1589,7 @@
        item = heroItem.GetItem()
        for lpIndex in range(lineupCount)[::-1]:
            lineupValue = item.GetUserAttrByIndex(ShareDefine.Def_IudetHeroLineup, lpIndex)
            if GetLineupValue(lineupValue)[0] != lineupID:
            if GetLineupValue(lineupValue)[0] != presetID:
                continue
            item.DelUserAttr(ShareDefine.Def_IudetHeroLineup, lineupValue)
            delCount += 1
@@ -1344,21 +1612,17 @@
            continue
        heroIDList.append(itemID)
        item = heroItem.GetItem()
        lineupValue = ComLineupValue(lineupID, shapeType, posNum)
        lineupValue = ComLineupValue(presetID, shapeType, posNum)
        item.AddUserAttr(ShareDefine.Def_IudetHeroLineup, lineupValue)
        if itemIndex not in syncItemDict:
            syncItemDict[itemIndex] = heroItem
        heroItemDict[itemIndex] = posNum
        
    # 主阵容修改时重整背包,约定所有背包由前端自行排序
    #if lineupID == ShareDefine.Lineup_Main:
    #    ResetHeroPack(curPlayer)
    #else:
    # 约定所有背包由前端自行排序
    for syncItem in syncItemDict.values():
        syncItem.Sync_Item()
        
    lineup = PlayerOnline.GetOnlinePlayer(curPlayer).GetLineup(lineupID)
    lineup.UpdLineup(heroItemDict, shapeType)
    PlayerOnline.GetOnlinePlayer(curPlayer).UpdHeroItemPreset(presetID, heroItemDict, shapeType)
    return
def ComLineupValue(lineupID, shapeType, posNum): return lineupID * 10000 + shapeType * 100 + posNum
@@ -1374,6 +1638,200 @@
#    ItemControler.ResetItem(curPlayer, ShareDefine.rptHero, 0, 0, tick)
#    return
def GetLineupRecommendAward(curPlayer, recommendID, index):
    ## 领取阵容推荐奖励
    index = GameWorld.ToIntDef(index)
    awardState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroRecommend % recommendID)
    if awardState & pow(2, index):
        GameWorld.DebugLog("阵容推荐奖励已领取! recommendID=%s,index=%s" % (recommendID, index))
        return
    ipyData = IpyGameDataPY.GetIpyGameData("LineupRecommend", recommendID)
    if not ipyData:
        return
    heroIDList = ipyData.GetHeroIDList()
    if index >= len(heroIDList):
        return
    heroID = heroIDList[index]
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return
    quality = heroIpyData.GetQuality()
    qualityIpyData = IpyGameDataPY.GetIpyGameData("HeroQuality", quality)
    if not qualityIpyData:
        return
    awardMoneyInfo = qualityIpyData.GetRecommendAwardMoney()
    if not awardMoneyInfo or len(awardMoneyInfo) != 2:
        return
    moneyType, moneyValue = awardMoneyInfo
    if not GetHeroActivite(curPlayer, heroID):
        GameWorld.DebugLog("武将未获得过,不可激活阵容推荐! heroID=%s" % (heroID))
        return
    awardState |= pow(2, index)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroRecommend % recommendID, awardState)
    GameWorld.DebugLog("阵容推荐领奖! recommendID=%s,index=%s,heroID=%s,awardState=%s" % (recommendID, index, heroID, awardState))
    PlayerControl.GiveMoney(curPlayer, moneyType, moneyValue, "LineupRecommend", notifyAward=True)
    Sync_LineupRecommendInfo(curPlayer, [recommendID])
    return
def GetHeroFatesState(curPlayer, fatesID): # 宿缘ID状态: 0-未激活;1-已激活
    info = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroFatesInfo % fatesID)
    return info % 10
def SetHeroFatesState(curPlayer, fatesID, state):
    info = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroFatesInfo % fatesID)
    info = info / 10 * 10 + min(state, 9)
    info = PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroFatesInfo % fatesID, info)
    return info
def GetHeroFatesLV(curPlayer, fatesID): # 宿缘ID等级
    info = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroFatesInfo % fatesID)
    return info / 10
def SetHeroFatesLV(curPlayer, fatesID, lv):
    info = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroFatesInfo % fatesID)
    info = lv * 10 + info % 10
    info = PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroFatesInfo % fatesID, info)
    return info
#// B2 41 武将宿缘 #tagCSHeroFates
#
#struct    tagCSHeroFates
#{
#    tagHead         Head;
#    BYTE        FatesID;        // 宿缘ID
#    BYTE        OPType;        // 0-激活领奖;1-升级
#    BYTE        IndexCnt;
#    WORD        ItemIndexList[IndexCnt];    // 升级时消耗的材料卡在武将背包索引列表,升级时才发
#};
def OnHeroFates(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    fatesID = clientData.FatesID
    opType = clientData.OPType
    itemIndexList = clientData.ItemIndexList
    if opType == 1:
        __onHeroFatesLVUP(curPlayer, fatesID, itemIndexList)
    else:
        __onHeroFatesActivite(curPlayer, fatesID)
    return
def __onHeroFatesActivite(curPlayer, fatesID):
    ## 宿缘激活
    if GetHeroFatesState(curPlayer, fatesID):
        GameWorld.DebugLog("宿缘组合已经激活了! fatesID=%s" % fatesID)
        return
    ipyData = IpyGameDataPY.GetIpyGameData("HeroFates", fatesID)
    if not ipyData:
        return
    heroIDList = ipyData.GetHeroIDList()
    for heroID in heroIDList:
        if not GetHeroActivite(curPlayer, heroID):
            GameWorld.DebugLog("有武将未获得过,不可激活宿缘! fatesID=%s,heroID=%s,heroIDList=%s" % (fatesID, heroID, heroIDList))
            return
    GameWorld.DebugLog("激活宿缘! fatesID=%s,heroIDList=%s" % (fatesID, heroIDList))
    SetHeroFatesState(curPlayer, fatesID, 1)
    itemList = ipyData.GetAwardItemList()
    ItemControler.GivePlayerItemOrMail(curPlayer, itemList, event=["HeroFates", False, {}])
    Sync_HeroFatesInfo(curPlayer, [fatesID])
    return
def __onHeroFatesLVUP(curPlayer, fatesID, useIndexList):
    if not GetHeroFatesState(curPlayer, fatesID):
        GameWorld.DebugLog("宿缘组合未激活! fatesID=%s" % fatesID)
        return
    ipyData = IpyGameDataPY.GetIpyGameData("HeroFates", fatesID)
    if not ipyData:
        return
    fatesQuality = ipyData.GetFatesQuality()
    heroIDList = ipyData.GetHeroIDList()
    fatesNextLV = GetHeroFatesLV(curPlayer, fatesID) + 1
    GameWorld.DebugLog("宿缘升级: fatesID=%s,fatesQuality=%s,fatesNextLV=%s,heroIDList=%s,useIndexList=%s" % (fatesID, fatesQuality, fatesNextLV, heroIDList, useIndexList))
    qualityLVIpyData = IpyGameDataPY.GetIpyGameData("HeroFatesQualityLV", fatesQuality, fatesNextLV)
    if not qualityLVIpyData:
        return
    needStarTotal = qualityLVIpyData.GetNeedStarTotal()
    needHeroCnt = qualityLVIpyData.GetNeedHeroCnt()
    needQuality = qualityLVIpyData.GetNeedQuality()
    costItemList = []
    heroStarDict = {}
    olPlayer = PlayerOnline.GetOnlinePlayer(curPlayer)
    curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
    for index in range(curPack.GetCount()):
        heroItem = curPack.GetAt(index)
        if not heroItem or heroItem.IsEmpty():
            continue
        heroID = heroItem.GetItemTypeID()
        # 材料卡
        if index in useIndexList:
            if __checkHeroFatesLVUPItem(olPlayer, needQuality, index, heroItem, heroID):
                costItemList.append(heroItem)
        if heroID not in heroIDList:
            continue
        if heroID not in heroStarDict:
            heroStarDict[heroID] = 0
        star = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
        if star <= heroStarDict[heroID]:
            continue
        heroStarDict[heroID] = star
    nowStarTotal = sum(heroStarDict.values())
    if nowStarTotal < needStarTotal:
        GameWorld.DebugLog("    当前总星级不足: nowStarTotal=%s < %s, heroStarDict=%s" % (nowStarTotal, needStarTotal, heroStarDict))
        return
    GameWorld.DebugLog("    当前总星级: nowStarTotal=%s,needStarTotal=%s,heroStarDict=%s" % (nowStarTotal, needStarTotal, heroStarDict))
    if len(costItemList) < needHeroCnt:
        GameWorld.DebugLog("    可用材料卡不足: %s < %s" % (len(costItemList), needHeroCnt))
        return
    GameWorld.DebugLog("    宿缘升级! needHeroCnt=%s" % needHeroCnt)
    for heroItem in costItemList[:needHeroCnt]:
        ItemCommon.DelItem(curPlayer, heroItem, heroItem.GetCount(), False, "HeroFatesLVUP")
    SetHeroFatesLV(curPlayer, fatesID, fatesNextLV)
    Sync_HeroFatesInfo(curPlayer, [fatesID])
    RefreshLordAttr(curPlayer) # 宿缘
    return
def __checkHeroFatesLVUPItem(olPlayer, needQuality, itemIndex, heroItem, heroID):
    ## 检查宿缘材料卡可否使用
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return
    quality = heroIpyData.GetQuality()
    if quality != needQuality:
        GameWorld.DebugLog("    与宿缘所需品质不同的卡无法使用: itemIndex=%s,heroID=%s,quality=%s != %s" % (itemIndex, heroID, quality, needQuality))
        return
    #未生效、未上阵、未锁定、未进行过升级、突破、升星、觉醒
    heroLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroLV)
    breakLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
    starLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
    awakeLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
    if heroLV > 1 or breakLV or starLV or awakeLV:
        GameWorld.DebugLog("    升级突破升星觉醒过的武将无法使用! itemIndex=%s,heroLV=%s,breakLV=%s,starLV=%s,awakeLV=%s" % (itemIndex, heroLV, breakLV, starLV, awakeLV))
        return
    if heroItem.GetIsLocked():
        GameWorld.DebugLog("    锁定的武将无法使用! itemIndex=%s" % (itemIndex))
        return
    lineupCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroLineup)
    if lineupCount:
        lineupValueList = [heroItem.GetUserAttrByIndex(ShareDefine.Def_IudetHeroLineup, lpIndex) for lpIndex in range(lineupCount)]
        GameWorld.DebugLog("    上阵中的武将无法使用! itemIndex=%s,lineupValueList=%s" % (itemIndex, lineupValueList))
        return
    heroID = heroItem.GetItemTypeID()
    effPresetIDList = GetHeroEffPresetIDList(heroItem)
    if effPresetIDList:
        GameWorld.DebugLog("    生效中的卡牌无法使用! itemIndex=%s,heroID=%s,effPresetIDList=%s" % (itemIndex, heroID, effPresetIDList))
        return
    return True
def RefreshLordAttr(curPlayer):
    ## 刷新主公属性
    CalcHeroAddAttr(curPlayer)
@@ -1383,31 +1841,51 @@
def CalcHeroAddAttr(curPlayer):
    ## 计算武将对主公增加的属性
    
    heroBookAttrDict = {}
    skinAttrDict = {}
    fatesAttrDict = {}
    playerID = curPlayer.GetID()
    
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for index in range(ipyDataMgr.GetHeroCount()):
        ipyData = ipyDataMgr.GetHeroByIndex(index)
        heroID = ipyData.GetHeroID()
        if not GetHeroBookInitState(curPlayer, heroID):
            # 图鉴未激活
    # 武将时装
    for index in range(ipyDataMgr.GetHeroSkinAttrCount()):
        ipyData = ipyDataMgr.GetHeroSkinAttrByIndex(index)
        skinID = ipyData.GetSkinID()
        if not GetHeroSkinState(curPlayer, skinID):
            continue
        quality = ipyData.GetQuality()
        qualityIpyData = IpyGameDataPY.GetIpyGameData("HeroQuality", quality)
        if not qualityIpyData:
            continue
        bookInitAddPer = qualityIpyData.GetBookInitAddPer()
        bookStarAddPer = qualityIpyData.GetBookStarAddPer()
        bookBreakLVAddPer = qualityIpyData.GetBookBreakLVAddPer()
        bookStar = GetHeroBookStarLV(curPlayer, heroID)
        bookBreakLV = GetHeroBookBreakLV(curPlayer, heroID)
        for attrPerID in ChConfig.BaseAttrPerIDList:
            addPer = bookInitAddPer + bookStar * bookStarAddPer + bookBreakLV * bookBreakLVAddPer
            heroBookAttrDict[attrPerID] = heroBookAttrDict.get(attrPerID, 0) + addPer
        skinStar = GetHeroSkinStar(curPlayer, skinID)
        attrIDList = ipyData.GetRoleAttrIDList()
        attrValueList = ipyData.GetRoleAttrValueList()
        perStarAddList = ipyData.GetRoleAttrPerStarAddList()
        for i in range(min(len(attrIDList), len(attrValueList))):
            attrID = attrIDList[i]
            attrValuePerStar = perStarAddList[i] if len(perStarAddList) > i else 0
            attrValue = attrValueList[i] + attrValuePerStar * skinStar
            skinAttrDict[attrID] = skinAttrDict.get(attrID, 0) + attrValue
            
    GameWorld.DebugLog("武将图鉴属性: %s" % heroBookAttrDict, playerID)
    PlayerOnline.GetOnlinePlayer(curPlayer).SetCalcAttr(ChConfig.Def_CalcAttr_HeroBook, heroBookAttrDict)
    GameWorld.DebugLog("时装属性: %s" % skinAttrDict, playerID)
    PlayerOnline.GetOnlinePlayer(curPlayer).SetCalcAttr(ChConfig.Def_CalcAttr_HeroSkin, skinAttrDict)
    # 宿缘
    for index in range(ipyDataMgr.GetHeroFatesCount()):
        ipyData = ipyDataMgr.GetHeroFatesByIndex(index)
        fatesID = ipyData.GetFatesID()
        if not GetHeroFatesState(curPlayer, fatesID):
            continue
        fatesLV = GetHeroFatesLV(curPlayer, fatesID)
        if fatesLV <= 0:
            continue
        attrIDList = ipyData.GetAttrIDList()
        lvAttrValueList = ipyData.GetLVAttrValueList()
        for i in range(min(len(attrIDList), len(lvAttrValueList))):
            attrID = attrIDList[i]
            attrValuePerLV = lvAttrValueList[i]
            attrValue = attrValuePerLV * fatesLV
            fatesAttrDict[attrID] = fatesAttrDict.get(attrID, 0) + attrValue
    GameWorld.DebugLog("宿缘属性: %s" % fatesAttrDict, playerID)
    PlayerOnline.GetOnlinePlayer(curPlayer).SetCalcAttr(ChConfig.Def_CalcAttr_HeroFates, fatesAttrDict)
    return
def Sync_HeroInfo(curPlayer, heroIDList=None):
@@ -1418,6 +1896,8 @@
        ipyDataMgr = IpyGameDataPY.IPY_Data()
        for index in range(ipyDataMgr.GetHeroCount()):
            ipyData = ipyDataMgr.GetHeroByIndex(index)
            if not ipyData.GetPlayerCanUse():
                continue
            syncHeroIDList.append(ipyData.GetHeroID())
    
    if not syncHeroIDList:
@@ -1430,13 +1910,19 @@
            continue
        if heroIDList == None and not curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID):
            continue
        skinIDList = heroIpyData.GetSkinIDList()
        
        hero = ChPyNetSendPack.tagSCHero()
        hero.HeroID = heroID
        hero.SkinState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroSkin % heroID)
        hero.BookInitState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID) % 10
        hero.BookStarLV = GetHeroBookStarLV(curPlayer, heroID)
        hero.BookBreakLV = GetHeroBookBreakLV(curPlayer, heroID)
        hero.SkinList = []
        for skinID in skinIDList[1:]: # 第1个默认激活的不同步
            skin = ChPyNetSendPack.tagSCHeroSkin()
            skin.SkinID = skinID
            skin.State = GetHeroSkinState(curPlayer, skinID)
            skin.Star = GetHeroSkinStar(curPlayer, skinID)
            hero.SkinList.append(skin)
        hero.SkinCnt = len(hero.SkinList)
        syncInfoList.append(hero)
        
    if not syncInfoList:
@@ -1448,39 +1934,39 @@
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
def Sync_Lineup(curPlayer, lineupID=None):
    if lineupID:
        syncIDList = [lineupID]
    else:
        syncIDList = ShareDefine.LineupList
    lineupList = []
def Sync_HeroPreset(curPlayer, heroPresetID=None):
    olPlayer = PlayerOnline.GetOnlinePlayer(curPlayer)
    for lineupID in syncIDList:
        lineup = olPlayer.GetLineup(lineupID)
        if not lineup:
    if heroPresetID:
        syncIDList = [heroPresetID]
    else:
        syncIDList = olPlayer.GetHeroPresetIDList()
    presetList = []
    for heroPresetID in syncIDList:
        heroPreset = olPlayer.GetHeroPreset(heroPresetID)
        if not heroPreset:
            continue
        
        posNumItemIndexDict = {v:k for k, v in lineup.heroItemDict.items()}
        posNumItemIndexDict = {v:k for k, v in heroPreset.heroItemDict.items()}
        heroItemIndexList = [] # 所在武将背包索引+1列表 [站位1物品索引+1, 站位2, ...],站位无武将时为0
        for posNum in range(1, 1 + ShareDefine.LineupObjMax):
            if posNum in posNumItemIndexDict:
                heroItemIndexList.append(posNumItemIndexDict[posNum] + 1)
            else:
                heroItemIndexList.append(0)
        packLineup = ChPyNetSendPack.tagSCLineup()
        packLineup.LineupID = lineup.lineupID
        packLineup.ShapeType = lineup.shapeType
        packLineup.HeroItemIndexList = heroItemIndexList
        packLineup.HeroCnt = len(packLineup.HeroItemIndexList)
        lineupList.append(packLineup)
        preset = ChPyNetSendPack.tagSCHeroPreset()
        preset.PresetID = heroPresetID
        #preset.ShapeType = heroPreset.shapeType
        preset.HeroItemIndexList = heroItemIndexList
        preset.HeroCnt = len(preset.HeroItemIndexList)
        presetList.append(preset)
        
    if not lineupList:
    if not presetList:
        return
    
    clientPack = ChPyNetSendPack.tagSCLineupInfo()
    clientPack.LineupList = lineupList
    clientPack.LineupCnt = len(clientPack.LineupList)
    clientPack = ChPyNetSendPack.tagSCHeroPresetInfo()
    clientPack.PresetList = presetList
    clientPack.PresetCnt = len(clientPack.PresetList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
@@ -1490,3 +1976,58 @@
    clientPack.AwakeRebirthCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroAwakeRebirthCnt)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
def Sync_LineupRecommendInfo(curPlayer, syncIDList=None):
    recommendList = []
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for index in range(ipyDataMgr.GetLineupRecommendCount()):
        ipyData = ipyDataMgr.GetLineupRecommendByIndex(index)
        recommendID = ipyData.GetRecommendID()
        awardState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroRecommend % recommendID)
        if syncIDList != None:
            if recommendID not in syncIDList:
                continue
        elif not awardState:
            continue
        recommend = ChPyNetSendPack.tagSCLineupRecommend()
        recommend.RecommendID = recommendID
        recommend.AwardState = awardState
        recommendList.append(recommend)
    if not recommendList:
        return
    clientPack = ChPyNetSendPack.tagSCLineupRecommendInfo()
    clientPack.RecommendList = recommendList
    clientPack.Count = len(clientPack.RecommendList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
def Sync_HeroFatesInfo(curPlayer, syncIDList=None):
    fatesList = []
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for index in range(ipyDataMgr.GetHeroFatesCount()):
        ipyData = ipyDataMgr.GetHeroFatesByIndex(index)
        fatesID = ipyData.GetFatesID()
        state = GetHeroFatesState(curPlayer, fatesID)
        if syncIDList != None:
            if fatesID not in syncIDList:
                continue
        elif not state:
            continue
        fates = ChPyNetSendPack.tagSCHeroFates()
        fates.FatesID = fatesID
        fates.State = state
        fates.FatesLV = GetHeroFatesLV(curPlayer, fatesID)
        fatesList.append(fates)
    if not fatesList:
        return
    clientPack = ChPyNetSendPack.tagSCHeroFatesInfo()
    clientPack.FatesList = fatesList
    clientPack.Count = len(clientPack.FatesList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return