ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHero.py
@@ -20,16 +20,89 @@
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 GameWorld
import ChConfig
import random
def PlayerOnDay(curPlayer):
    if curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroAwakeRebirthCnt):
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroAwakeRebirthCnt, 0)
        Sync_PlayerHeroInfo(curPlayer)
    return
def OnPlayerLogin(curPlayer):
    Sync_HeroInfo(curPlayer)
    Sync_PlayerHeroInfo(curPlayer)
    Sync_LineupRecommendInfo(curPlayer)
    Sync_HeroFatesInfo(curPlayer)
    __CheckOSAHeroStar(curPlayer)
    return
def OnPlayerFirstLogin(curPlayer):
    OnFirstLoginInitPlayer(curPlayer)
    OnFirstLoginInitHero(curPlayer)
    return
def OnFirstLoginInitPlayer(curPlayer):
    ## 初始化主公
    equipPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptEquip)
    if not equipPack.GetCount():
        identifyPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptIdentify)
        if not identifyPack.GetCount():
            return
    defaultEquipInfo = IpyGameDataPY.GetFuncEvalCfg("NewRoleInit", 1, {})
    if not defaultEquipInfo:
        return
    GameWorld.DebugLog("初始化新手定制装备: %s" % defaultEquipInfo, curPlayer.GetPlayerID())
    for equipID, appointID in defaultEquipInfo.items():
        itemData = GameWorld.GetGameData().GetItemByTypeID(equipID)
        if not itemData:
            continue
        equipPlace = itemData.GetEquipPlace()
        equipPlaceIndex = equipPlace - 1 # 暂固定直接装备位-1
        if equipPlaceIndex < 0 or equipPlaceIndex >= equipPack.GetCount():
            continue
        destEquip = equipPack.GetAt(equipPlaceIndex)
        if not destEquip.IsEmpty():
            continue
        setAttrDict = {ShareDefine.Def_CItemKey_AppointID:appointID} if appointID else {}
        curItem = ItemControler.GetOutPutItemObj(equipID, 1, curPlayer=curPlayer, setAttrDict=setAttrDict)
        if not curItem:
            continue
        destEquip.AssignItem(curItem)
    return
def OnFirstLoginInitHero(curPlayer):
    ## 初始化默认武将阵型
    curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
    GameWorld.DebugLog("OnFirstLoginInitHero: %s" % curPack.GetCount(), curPlayer.GetPlayerID())
    if not curPack.GetCount():
        return
    defaultHeroInfo = IpyGameDataPY.GetFuncEvalCfg("NewRoleInit", 2, {})
    if not defaultHeroInfo:
        return
    GameWorld.DebugLog("初始化新手武将: %s" % defaultHeroInfo, curPlayer.GetPlayerID())
    presetID = 1 # 默认预设1
    shapeType = 0
    for heroID, posNum in defaultHeroInfo.items():
        lineupValue = ComLineupValue(presetID, shapeType, posNum)
        setAttrDict = {ShareDefine.Def_IudetHeroLineup:[lineupValue]}
        ItemControler.GivePlayerItem(curPlayer, heroID, 1, False, [ShareDefine.rptHero], setAttrDict=setAttrDict)
    return
def InitHeroItem(singleItem):
@@ -43,6 +116,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)
@@ -116,6 +191,8 @@
        GameWorld.DebugLog("首次激活武将: heroID=%s" % (heroID), curPlayer.GetPlayerID())
        #首次获得图鉴额外逻辑 ...
        Sync_HeroInfo(curPlayer, [heroID])
        PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 首次获得
        OpenServerActivity.UpdOSA_HeroTrainBillboard(curPlayer) # 首次获得
        
    return
@@ -157,29 +234,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)
@@ -192,20 +261,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)
        #阵容类型*10000+阵型类型*100+位置编号
        if lineupValue / 10000 != lineupID:
            continue
        return lineupValue % 100
    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
#
@@ -228,12 +293,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
@@ -258,9 +319,12 @@
    GameWorld.DebugLog("武将升级: itemIndex=%s,heroID=%s,updHeroLV=%s" % (itemIndex, heroID, updHeroLV), playerID)
    heroItem.SetUserAttr(ShareDefine.Def_IudetHeroLV, updHeroLV)
    
    # 刷属性
    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
        RefreshLordAttr(curPlayer)
    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):
@@ -270,12 +334,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
#
@@ -312,10 +374,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)
@@ -324,7 +390,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)
@@ -336,11 +402,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
@@ -350,10 +438,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
@@ -363,30 +456,53 @@
    starMax = InitStarUpper + addStarUpper
    return starMax
def DoHeroUpdStar(curPlayer, heroItem, updStar):
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)
    heroItem.Sync_Item()
    # 刷属性
    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
        RefreshLordAttr(curPlayer)
        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()
    #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)
@@ -395,14 +511,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:
@@ -434,6 +552,7 @@
            idList[zeroIndex] = randTalentID
            lvList[zeroIndex] = 1
            unfullLVIDList.append(randTalentID)
            unfullLVIDListUnlock.append(randTalentID)
            GameWorld.DebugLog("新增星级天赋ID: %s" % (randTalentID))
            addLV -= 1
            haveUp = True
@@ -443,9 +562,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]
@@ -456,8 +575,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
            
@@ -471,6 +593,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
@@ -492,39 +649,49 @@
    if not heroIpyData:
        return
    quality = heroIpyData.GetQuality()
    heroLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroLV)
    breakLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
    GameWorld.DebugLog("请求武将突破: itemIndex=%s,heroID=%s,quality=%s,breakLV=%s"
                       % (itemIndex, heroID, quality, breakLV), playerID)
    GameWorld.DebugLog("请求武将突破: itemIndex=%s,heroID=%s,quality=%s,heroLV=%s,breakLV=%s"
                       % (itemIndex, heroID, quality, heroLV, breakLV), playerID)
    ipyData = IpyGameDataPY.GetIpyGameData("HeroQualityBreak", quality, breakLV)
    if not ipyData:
        return
    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")
    
    # 刷属性
    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
        RefreshLordAttr(curPlayer)
    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):
def SetHeroBreakLV(curPlayer, heroItem, breakLV, isSync=True):
    ## 设置武将突破等级
    heroItem.SetUserAttr(ShareDefine.Def_IudetHeroBreakLV, breakLV)
    #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
@@ -574,27 +741,27 @@
        GameWorld.DebugLog("材料不足,武将无法觉醒! costItemID=%s, costItemCount=%s" % (costItemID, costItemCount))
        return
    ItemCommon.ReduceItem(curPlayer, itemPack, itemIndexList, costItemCount, True, "HeroAwake")
    GameWorld.DebugLog("武将觉醒: itemIndex=%s,heroID=%s,nextBreakLV=%s" % (itemIndex, heroID, nextAwakeLV), playerID)
    GameWorld.DebugLog("武将觉醒: itemIndex=%s,heroID=%s,nextAwakeLV=%s" % (itemIndex, heroID, nextAwakeLV), playerID)
    SetHeroAwakeLV(heroItem, nextAwakeLV)
    
    # 刷属性
    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
        RefreshLordAttr(curPlayer)
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 觉醒
    OpenServerActivity.UpdOSA_HeroTrainBillboard(curPlayer) # 觉醒
    return
def SetHeroAwakeLV(heroItem, awakeLV):
def SetHeroAwakeLV(heroItem, awakeLV, isSync=True):
    ## 设置武将觉醒等级
    item = heroItem.GetItem()
    item.SetUserAttr(ShareDefine.Def_IudetHeroAwakeLV, awakeLV)
    unlockTalentSlotByAwake(item)
    heroItem.Sync_Item()
    if isSync:
        heroItem.Sync_Item()
    return
def unlockTalentSlotByAwake(singleItem):
    ## 觉醒解锁天赋槽
    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
@@ -661,9 +828,13 @@
#};
def OnHeroAwakeSelectTalent(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    playerID = curPlayer.GetPlayerID()
    itemIndex = clientData.ItemIndex
    selectIndex = clientData.SelectIndex
    doSelectAwakeTalent(curPlayer, itemIndex, selectIndex)
    return
def doSelectAwakeTalent(curPlayer, itemIndex, selectIndex, isSync=True):
    playerID = curPlayer.GetPlayerID()
    heroItem = GetHeroItem(curPlayer, itemIndex)
    if not heroItem:
        return
@@ -686,34 +857,63 @@
        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):
        singleItem.AddUserAttr(ShareDefine.Def_IudetHeroTalentID, talentID)
        singleItem.AddUserAttr(ShareDefine.Def_IudetHeroTalentIDLV, lvList[index])
    unlockTalentSlotByAwake(singleItem)
    
    heroItem.Sync_Item()
    if isSync:
        heroItem.Sync_Item()
    
    # 刷属性
    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
        RefreshLordAttr(curPlayer)
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate(heroItem) # 选择天赋
    return
#// B2 35 武将洗炼 #tagCSHeroWash
@@ -755,33 +955,27 @@
        GameWorld.ErrLog("武将洗炼结果未处理,无法洗炼! washIDCnt=%s" % washIDCnt)
        return
    
    washCostItemInfo = IpyGameDataPY.GetFuncEvalCfg("HeroWash", 1)
    lockCostItemInfo = IpyGameDataPY.GetFuncEvalCfg("HeroWash", 2)
    if not washCostItemInfo or not lockCostItemInfo:
    washItemID = IpyGameDataPY.GetFuncCfg("HeroWash", 1)
    lockCostItemList = IpyGameDataPY.GetFuncEvalCfg("HeroWash", 2)
    if not lockCostItemList:
        return
    washItemID, washCostItemCount = washCostItemInfo
    lockItemID, lockCostItemCount = lockCostItemInfo
    singleItem = heroItem.GetItem()
    idCount = singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentID)
    for lockIndex in lockTalentIndexs[::-1]:
        if lockIndex >= idCount:
            lockTalentIndexs.remove(lockIndex)
            GameWorld.DebugLog("去除不存在的锁定索引: lockIndex=%s" % lockIndex)
    lockCnt = len(lockTalentIndexs)
    washCostItemCount = lockCostItemList[lockCnt] if len(lockCostItemList) > lockCnt else lockCostItemList[-1]
    GameWorld.DebugLog("washItemID=%s,washCostItemCount=%s,lockTalentIndexs=%s" % (washItemID, washCostItemCount, lockTalentIndexs))
    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
    hasEnough, washItemIndexList = ItemCommon.GetItem_FromPack_ByID(washItemID, itemPack, washCostItemCount)
    if not hasEnough:
        GameWorld.DebugLog("洗炼材料不足,武将无法洗炼! washItemID=%s,washCostItemCount=%s" % (washItemID, washCostItemCount))
        return
    lockItemIndexList = []
    lockCostItemCountTotal = 0
    if lockTalentIndexs:
        lockCostItemCountTotal = len(lockTalentIndexs) * lockCostItemCount
        hasEnough, lockItemIndexList = ItemCommon.GetItem_FromPack_ByID(lockItemID, itemPack, lockCostItemCountTotal)
        if not hasEnough:
            GameWorld.DebugLog("锁定材料不足,武将无法洗炼! lockItemID=%s,lockCostItemCount=%s,lockCostItemCountTotal=%s"
                               % (lockItemID, lockCostItemCount, lockCostItemCountTotal))
            return
    ItemCommon.ReduceItem(curPlayer, itemPack, washItemIndexList, washCostItemCount, True, "HeroTalentWash")
    ItemCommon.ReduceItem(curPlayer, itemPack, lockItemIndexList, lockCostItemCountTotal, True, "HeroTalentWash")
    
    singleItem = heroItem.GetItem()
    idCount = singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentID)
    washIDList = []
    for index in range(idCount):
        if index in lockTalentIndexs:
@@ -829,6 +1023,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):
@@ -836,8 +1032,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)
@@ -849,23 +1049,47 @@
    heroItem.Sync_Item()
    GameWorld.DebugLog("武将洗炼替换! itemIndex=%s,heroID=%s,washIDList=%s" % (itemIndex, heroID, washIDList))
    
    # 刷属性
    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
        RefreshLordAttr(curPlayer)
    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
@@ -878,32 +1102,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)
    # 刷属性
    if GetHeroLineupPosNum(heroItem, ShareDefine.Lineup_Main):
        RefreshLordAttr(curPlayer)
        
    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
@@ -918,16 +1199,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):
    ## 图鉴激活
@@ -952,53 +1246,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()
    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()
    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
@@ -1019,44 +1273,295 @@
    heroItem.SetIsLocked(1 if isLock else 0)
    return
#// B4 12 战斗阵容保存 #tagCSHeroLineupSave
#// B2 39 武将重生 #tagCSHeroRebirth
#
#struct    tagCSHeroLineupPos
#struct    tagCSHeroRebirth
#{
#    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
    heroLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroLV)
    breakLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
    awakeLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
    if heroLV <= 1 and not breakLV and not awakeLV:
        GameWorld.DebugLog("该武将未进行过等级突破觉醒培养,不需要重生! itemIndex=%s" % (itemIndex))
        return
    if awakeReset and awakeLV:
        rebirthCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroAwakeRebirthCnt)
        rebirthCntMax = IpyGameDataPY.GetFuncCfg("HeroRebirth", 2)
        if rebirthCntMax and rebirthCnt >= rebirthCntMax:
            GameWorld.DebugLog("今日觉醒过的武将重生次数已达上限! rebirthCnt=%s >= %s" % (rebirthCnt, rebirthCntMax))
            return
    heroID = heroItem.GetItemTypeID()
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return
    quality = heroIpyData.GetQuality()
    lvCostMoney = 0
    breakCostMoney = 0
    awakeCostMoney = 0
    moneyType = IpyGameDataPY.GetFuncCfg("HeroRebirth", 1)
    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
    # 验证通过,可以重生
    ratio = IpyGameDataPY.GetFuncCfg("HeroRebirth", 4)
    returnItemDict = {}
    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()
    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 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, ratio):
    ## 计算武将等级返还
    returnDict = {}
    for retLV in range(1, heroLV):
        qualityLVIpyData = IpyGameDataPY.GetIpyGameData("HeroQualityLV", quality, retLV)
        if not qualityLVIpyData:
            continue
        costItemInfo = qualityLVIpyData.GetUPCostItem()
        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,ratio=%s,%s,总%s" % (quality, heroLV, ratio, returnDict, returnItemDict))
    return
def __calcHeroBreakReturnitem(quality, breakLV, returnItemDict, ratio):
    # 计算武将突破返还
    returnDict = {}
    for retBreakLV in range(0, breakLV):
        qualityBreakIpyData = IpyGameDataPY.GetIpyGameData("HeroQualityBreak", quality, retBreakLV)
        if not qualityBreakIpyData:
            continue
        costItemList = qualityBreakIpyData.GetUPCostItemList()
        if not costItemList:
            continue
        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, ratio):
    # 计算武将觉醒返还
    returnDict = {}
    for retAwakeLV in range(0, awakeLV):
        qualityAwakeIpyData = IpyGameDataPY.GetIpyGameData("HeroQualityAwake", quality, retAwakeLV)
        if not qualityAwakeIpyData:
            continue
        costItemInfo = qualityAwakeIpyData.GetUPCostItem()
        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,ratio=%s,%s,总%s" % (quality, awakeLV, ratio, returnDict, returnItemDict))
    return
#// B2 40 武将遣散 #tagCSHeroDismiss
#
#struct    tagCSHeroDismiss
#{
#    tagHead         Head;
#    WORD        Count;
#    WORD        ItemIndexList[Count];    // 武将物品所在武将背包位置索引列表
#};
def OnHeroDismiss(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    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():
            continue
        heroItem = curPack.GetAt(itemIndex)
        if not heroItem or heroItem.IsEmpty():
            continue
        awakeLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
        if awakeLV:
            GameWorld.DebugLog("觉醒过的武将需先重生后才可遣散! itemIndex=%s,awakeLV=%s" % (itemIndex, awakeLV))
            continue
        if heroItem.GetIsLocked():
            GameWorld.DebugLog("锁定的武将无法遣散! itemIndex=%s" % (itemIndex))
            continue
        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))
            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
        quality = heroIpyData.GetQuality()
        heroLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroLV)
        breakLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
        heroStar = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
        qualityIpyData = IpyGameDataPY.GetIpyGameData("HeroQuality", quality)
        if not qualityIpyData:
            continue
        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 = max(1, int((heroStar + 1) * itemCount * ratio / 100.0))
            returnItemDict[itemID] = returnItemDict.get(itemID, 0) + starRetCnt
        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:
        return
    for itemIndex, heroItem in dismissItemList:
        ItemCommon.DelItem(curPlayer, heroItem, heroItem.GetCount(), False, "HeroDismiss")
    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
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(round(itemCount * retPer / 100.0)))
            returnItemExDict[key] = returnItemExDict.get(key, 0) + retCnt
        GameWorld.DebugLog("    红颜返还: retPer=%s,%s,总%s" % (retPer, beautyReturnItems, returnItemExDict))
    # 称号特殊效果
    return
#// B4 12 战斗阵容预设保存 #tagCSHeroPresetSave
#
#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
@@ -1071,8 +1576,7 @@
        item = heroItem.GetItem()
        for lpIndex in range(lineupCount)[::-1]:
            lineupValue = item.GetUserAttrByIndex(ShareDefine.Def_IudetHeroLineup, lpIndex)
            #阵容类型*10000+阵型类型*100+位置编号
            if lineupValue / 10000 != lineupID:
            if GetLineupValue(lineupValue)[0] != presetID:
                continue
            item.DelUserAttr(ShareDefine.Def_IudetHeroLineup, lineupValue)
            delCount += 1
@@ -1082,6 +1586,7 @@
            
    # 更新新阵型
    heroIDList = []
    heroItemDict = {}
    for posNum, itemIndex in heroPosDict.items():
        if itemIndex < 0 or itemIndex >= curPack.GetCount():
            continue
@@ -1094,43 +1599,280 @@
            continue
        heroIDList.append(itemID)
        item = heroItem.GetItem()
        lineupValue = lineupID * 10000 + shapeType * 100 + 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()
    RefreshLordAttr(curPlayer)
    # 约定所有背包由前端自行排序
    for syncItem in syncItemDict.values():
        syncItem.Sync_Item()
    PlayerOnline.GetOnlinePlayer(curPlayer).UpdHeroItemPreset(presetID, heroItemDict, shapeType)
    return
def ResetHeroPack(curPlayer):
    tick = GameWorld.GetGameWorld().GetTick()
    curPlayer.SetResetItemTick(0)
    ItemControler.ResetItem(curPlayer, ShareDefine.rptHero, 0, 0, tick)
def ComLineupValue(lineupID, shapeType, posNum): return lineupID * 10000 + shapeType * 100 + posNum
def GetLineupValue(lineupValue):
    lineupID = lineupValue / 10000
    shapeType = lineupValue % 10000 / 100
    posNum = lineupValue % 100
    return lineupID, shapeType, posNum
#def ResetHeroPack(curPlayer):
#    tick = GameWorld.GetGameWorld().GetTick()
#    curPlayer.SetResetItemTick(0)
#    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):
    ## 刷新主公属性
    CalcHeroItemAddAttr(curPlayer)
    CalcHeroAddAttr(curPlayer)
    PlayerOnline.GetOnlinePlayer(curPlayer).RefreshRoleAttr()
    return
def CalcHeroItemAddAttr(curPlayer):
    #allAttrListPet = [{} for _ in range(4)]
    return
def RefreshLineupHeroAttr(curPlayer):
    ## 刷新阵容武将属性
def CalcHeroAddAttr(curPlayer):
    ## 计算武将对主公增加的属性
    
    # 计算阵容总战力 = 角色总战力为主阵容战力,需同步计算不同阵容战力
    return
def CaclHeroCardAttr():
    skinAttrDict = {}
    fatesAttrDict = {}
    playerID = curPlayer.GetID()
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    # 武将时装
    for index in range(ipyDataMgr.GetHeroSkinAttrCount()):
        ipyData = ipyDataMgr.GetHeroSkinAttrByIndex(index)
        skinID = ipyData.GetSkinID()
        if not GetHeroSkinState(curPlayer, skinID):
            continue
        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" % 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):
@@ -1141,6 +1883,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:
@@ -1153,13 +1897,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:
@@ -1170,3 +1920,101 @@
    clientPack.HeroCnt = len(clientPack.HeroInfoList)    
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
def Sync_HeroPreset(curPlayer, heroPresetID=None):
    olPlayer = PlayerOnline.GetOnlinePlayer(curPlayer)
    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 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)
        preset = ChPyNetSendPack.tagSCHeroPreset()
        preset.PresetID = heroPresetID
        #preset.ShapeType = heroPreset.shapeType
        preset.HeroItemIndexList = heroItemIndexList
        preset.HeroCnt = len(preset.HeroItemIndexList)
        presetList.append(preset)
    if not presetList:
        return
    clientPack = ChPyNetSendPack.tagSCHeroPresetInfo()
    clientPack.PresetList = presetList
    clientPack.PresetCnt = len(clientPack.PresetList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
def Sync_PlayerHeroInfo(curPlayer):
    ## 武将公共信息
    clientPack = ChPyNetSendPack.tagSCPlayerHeroInfo()
    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