hxp
6 天以前 2b34924e06c0c36d77d9ccec4c4f10f1ebd16e84
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHero.py
@@ -23,13 +23,77 @@
import ChPyNetSendPack
import NetPackCommon
import PlayerControl
import PlayerOnline
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)
    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())
    lineupID = ShareDefine.Lineup_Main
    shapeType = 0
    for heroID, posNum in defaultHeroInfo.items():
        lineupValue = ComLineupValue(lineupID, shapeType, posNum)
        setAttrDict = {ShareDefine.Def_IudetHeroLineup:[lineupValue]}
        ItemControler.GivePlayerItem(curPlayer, heroID, 1, False, [ShareDefine.rptHero], setAttrDict=setAttrDict)
    return
def InitHeroItem(singleItem):
@@ -43,8 +107,6 @@
        singleItem.SetUserAttr(ShareDefine.Def_IudetHeroAwakeLV, 0)
    if singleItem.GetUserAttr(ShareDefine.Def_IudetHeroSkin):
        singleItem.SetUserAttr(ShareDefine.Def_IudetHeroSkin, 0)
    if singleItem.GetUserAttr(ShareDefine.Def_IudetHeroPosNum):
        singleItem.SetUserAttr(ShareDefine.Def_IudetHeroPosNum, 0)
        
    if singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentID):
        singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroTalentID)
@@ -56,6 +118,8 @@
        singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroTalentWashID)
    if singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentIDAwakeRand):
        singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroTalentIDAwakeRand)
    if singleItem.GetUserAttrCount(ShareDefine.Def_IudetHeroLineup):
        singleItem.ClearUserAttr(ShareDefine.Def_IudetHeroLineup)
        
    InitHeroTalent(singleItem)
    return
@@ -110,12 +174,101 @@
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return
    heroIndex = heroIpyData.GetHeroIndex()
    heroActState = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HeroActState, heroIndex)
    if not heroActState:
        GameWorld.SetDictValueByBit(curPlayer, ChConfig.Def_PDict_HeroActState, heroIndex, 1)
        GameWorld.DebugLog("首次激活武将: heroID=%s,heroIndex=%s" % (heroID, heroIndex), curPlayer.GetPlayerID())
    if not GetHeroActivite(curPlayer, heroID):
        SetHeroActivite(curPlayer, heroID, 1)
        GameWorld.DebugLog("首次激活武将: heroID=%s" % (heroID), curPlayer.GetPlayerID())
        #首次获得图鉴额外逻辑 ...
        Sync_HeroInfo(curPlayer, [heroID])
    return
def GetHeroActivite(curPlayer, heroID):
    ## 武将状态
    # @return: 0-未激活;1-武将已获得,可激活;2-图鉴已激活
    actState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID) % 10
    return actState
def SetHeroActivite(curPlayer, heroID, isAct=1):
    ## 设置武将已获得,可激活状态
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    actState = bookState % 10
    if isAct:
        if actState:
            return
        actState = 1
    else:
        actState = 0
    updBookState = GameWorld.SetValue(bookState, 1, 1, actState)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroBook % heroID, updBookState)
    GameWorld.DebugLog("设置武将激活状态:%s,bookState=%s,updBookState=%s" % (isAct, bookState, updBookState), curPlayer.GetPlayerID())
    return
def GetHeroBookInitState(curPlayer, heroID):
    ## 武将图鉴激活状态
    initState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID) % 10
    return initState > 1
def SetHeroBookInitState(curPlayer, heroID, isAct=1):
    ## 设置武将图鉴激活状态
    bookState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID)
    actState = bookState % 10
    if isAct:
        actState = 2
    else:
        actState = 1 if actState else 0
    updBookState = GameWorld.SetValue(bookState, 1, 1, actState)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroBook % heroID, updBookState)
    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 GetHeroBookStarLVH(curPlayer, heroID):
    ## 武将图鉴星级历史最高等级
    bookStateH = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBookH % heroID)
    return GameWorld.GetValue(bookStateH, 4, 3)
def SetHeroBookStarLVH(curPlayer, heroID, starLVH):
    ## 设置武将图鉴星级历史最高等级,支持三位数 0~999 级
    bookStateH = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBookH % heroID)
    updBookStateH = GameWorld.SetValue(bookStateH, 4, 3, starLVH)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroBookH % heroID, updBookStateH)
    GameWorld.DebugLog("设置武将图鉴星级历史最高等级:%s,bookStateH=%s,updBookStateH=%s" % (starLVH, bookStateH, updBookStateH), curPlayer.GetPlayerID())
    Sync_HeroInfo(curPlayer, [heroID])
    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 GetHeroBookBreakLVH(curPlayer, heroID):
    ## 武将图鉴突破历史最高等级
    bookStateH = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBookH % heroID)
    return GameWorld.GetValue(bookStateH, 7, 3)
def SetHeroBookBreakLVH(curPlayer, heroID, breakLVH):
    ## 设置武将图鉴突破历史最高等级,支持三位数 0~999 级
    bookStateH = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBookH % heroID)
    updBookStateH = GameWorld.SetValue(bookStateH, 7, 3, breakLVH)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroBookH % heroID, updBookStateH)
    GameWorld.DebugLog("设置武将图鉴突破历史最高等级:%s,bookStateH=%s,updBookStateH=%s" % (breakLVH, bookStateH, updBookStateH), curPlayer.GetPlayerID())
    Sync_HeroInfo(curPlayer, [heroID])
    return
def GetHeroItem(curPlayer, itemIndex):
@@ -128,6 +281,21 @@
    if heroItem.GetType() != ChConfig.Def_ItemType_Hero:
        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
#// B2 30 武将升级 #tagCSHeroLVUP
#
@@ -156,10 +324,14 @@
    if heroLV >= LVMax:
        GameWorld.DebugLog("该武将已满级!LVMax=%s" % (LVMax), playerID)
        return
    qualityIpyData = IpyGameDataPY.GetIpyGameData("HeroQuality", quality)
    if not qualityIpyData:
    qualityLVIpyData = IpyGameDataPY.GetIpyGameData("HeroQualityLV", quality, heroLV)
    if not qualityLVIpyData:
        return
    costItemInfo = qualityIpyData.GetUPCostItem()
    nextHeroLV = heroLV + 1
    if not IpyGameDataPY.GetIpyGameData("HeroQualityLV", quality, nextHeroLV):
        GameWorld.DebugLog("不存在该武将等级: quality=%s,nextHeroLV=%s" % (quality, nextHeroLV), playerID)
        return
    costItemInfo = qualityLVIpyData.GetUPCostItem()
    if not costItemInfo:
        return
    costItemID, costItemCount = costItemInfo
@@ -172,12 +344,11 @@
        return
    ItemCommon.ReduceItem(curPlayer, itemPack, itemIndexList, costItemCount, True, "HeroLVUP")
    
    updHeroLV = heroLV + 1
    updHeroLV = nextHeroLV
    GameWorld.DebugLog("武将升级: itemIndex=%s,heroID=%s,updHeroLV=%s" % (itemIndex, heroID, updHeroLV), playerID)
    heroItem.SetUserAttr(ShareDefine.Def_IudetHeroLV, updHeroLV)
    
    # 刷属性,之后扩展
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    return
def GetHeroLVMax(heroItem):
@@ -218,6 +389,9 @@
    if heroID != useHeroID:
        GameWorld.DebugLog("武将材料非本体,无法升星!", playerID)
        return
    if useItem.GetIsLocked():
        GameWorld.DebugLog("材料卡锁定中,无法升星! useItemIndex=%s,heroID=%s" % (useItemIndex, heroID), playerID)
        return
    washIDCnt = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentWashID)
    if washIDCnt:
        GameWorld.ErrLog("武将洗炼结果未处理,无法升星! itemIndex=%s,heroID=%s" % (itemIndex, heroID), playerID)
@@ -226,10 +400,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)
@@ -264,10 +442,13 @@
        return 0
    InitStarUpper = qualityIpyData.GetInitStarUpper()
    
    awakeLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
    if not awakeLV:
        return InitStarUpper
    addStarUpper = 0
    heroAwakeIpyDataList = IpyGameDataPY.GetIpyGameDataList("HeroAwake", heroID)
    heroAwakeIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("HeroAwake", heroID)
    if heroAwakeIpyDataList:
        awakeLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
        for ipyData in heroAwakeIpyDataList:
            if ipyData.GetAwakeLV() > awakeLV:
                break
@@ -277,29 +458,48 @@
    starMax = InitStarUpper + addStarUpper
    return starMax
def DoHeroUpdStar(curPlayer, heroItem, updStar):
def DoHeroUpdStar(curPlayer, heroItem, updStar, isSync=True):
    ## 执行武将星级更新
    heroID = heroItem.GetItemTypeID()
    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 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)
@@ -308,14 +508,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:
@@ -347,6 +549,7 @@
            idList[zeroIndex] = randTalentID
            lvList[zeroIndex] = 1
            unfullLVIDList.append(randTalentID)
            unfullLVIDListUnlock.append(randTalentID)
            GameWorld.DebugLog("新增星级天赋ID: %s" % (randTalentID))
            addLV -= 1
            haveUp = True
@@ -356,9 +559,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]
@@ -369,8 +572,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
            
@@ -405,11 +611,16 @@
    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
    LVMax = ipyData.GetLVMax()
    if heroLV < LVMax:
        GameWorld.DebugLog("未满级,无法突破: heroLV=%s < %s" % (heroLV, LVMax), playerID)
        return
    nextBreakLV = breakLV + 1
    if not IpyGameDataPY.GetIpyGameData("HeroQualityBreak", quality, nextBreakLV):
@@ -428,15 +639,22 @@
        return
    ItemCommon.ReduceItem(curPlayer, itemPack, itemIndexList, costItemCount, True, "HeroBreak")
    GameWorld.DebugLog("武将突破: itemIndex=%s,heroID=%s,nextBreakLV=%s" % (itemIndex, heroID, nextBreakLV), playerID)
    SetHeroBreakLV(heroItem, nextBreakLV)
    SetHeroBreakLV(curPlayer, heroItem, nextBreakLV)
    
    # 刷属性,之后扩展
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    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
@@ -486,26 +704,26 @@
        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)
    
    # 刷属性,之后扩展
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    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
@@ -572,9 +790,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
@@ -617,13 +839,13 @@
    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()
    
    # 刷属性,之后扩展
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    return
#// B2 35 武将洗炼 #tagCSHeroWash
@@ -665,33 +887,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:
@@ -746,8 +962,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)
@@ -759,8 +979,7 @@
    heroItem.Sync_Item()
    GameWorld.DebugLog("武将洗炼替换! itemIndex=%s,heroID=%s,washIDList=%s" % (itemIndex, heroID, washIDList))
    
    # 刷属性,之后扩展
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    return
#// B2 36 武将换肤 #tagCSHeroWearSkin
@@ -782,9 +1001,9 @@
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return
    skinNPCIDList = heroIpyData.GetSkinNPCIDList()
    skinIDList = heroIpyData.GetSkinIDList()
    if skinIndex > 0: # 0的为默认皮肤,不做限制
        if skinIndex >= len(skinNPCIDList):
        if skinIndex >= len(skinIDList):
            GameWorld.DebugLog("该武将不存在该皮肤! heroID=%s,skinIndex=%s" % (heroID, skinIndex))
            return
        skinState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroSkin % heroID)
@@ -793,8 +1012,7 @@
            return
    heroItem.SetUserAttr(ShareDefine.Def_IudetHeroSkin, skinIndex)
    
    # 刷属性
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    return
def ActiveHeroSkin(curPlayer, heroID, skinIndex, isActive=True):
@@ -809,26 +1027,326 @@
                           % (heroID, skinIndex, skinState, updState), curPlayer.GetPlayerID())
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroSkin % heroID, updState)
    Sync_HeroInfo(curPlayer, [heroID])
    RefreshLordAttr(curPlayer)
    return
#// B4 12 战斗阵型保存 #tagCSHeroBattlePosSave
#// B2 37 武将图鉴激活升级 #tagCSHeroBookUP
#
#struct    tagCSHeroBattlePos
#struct    tagCSHeroBookUP
#{
#    tagHead        Head;
#    DWORD        HeroID;        //武将ID
#    WORD        ItemIndex;    //关联武将物品所在武将背包索引,激活时可不用发
#    BYTE        BookType;    //图鉴激活类型: 0-初始激活;1-星级升级;2-突破等级升级
#};
def OnHeroBookUP(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    heroID = clientData.HeroID
    bookType = clientData.BookType
    if bookType == 1:
        __doHeroBookStarLVUP(curPlayer, heroID)
    elif bookType == 2:
        __doHeroBookBreakLVUP(curPlayer, heroID)
    else:
        __doHeroBookAct(curPlayer, heroID)
    return
def __doHeroBookAct(curPlayer, heroID):
    ## 图鉴激活
    playerID = curPlayer.GetPlayerID()
    if GetHeroBookInitState(curPlayer, heroID):
        GameWorld.DebugLog("该武将图鉴已激活! heroID=%s" % heroID, playerID)
        return
    GameWorld.DebugLog("武将图鉴激活! heroID=%s" % heroID, playerID)
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return
    quality = heroIpyData.GetQuality()
    qualityIpyData = IpyGameDataPY.GetIpyGameData("HeroQuality", quality)
    if not qualityIpyData:
        return
    if GetHeroActivite(curPlayer, heroID) != 1:
        GameWorld.DebugLog("武将未获得过,不可激活图鉴! heroID=%s" % (heroID), playerID)
        return
    SetHeroBookInitState(curPlayer, heroID, 1)
    awardMoneyInfo = qualityIpyData.GetBookActAwardMoney()
    if awardMoneyInfo and len(awardMoneyInfo) == 2:
        moneyType, moneyValue = awardMoneyInfo
        if moneyType and moneyValue:
            PlayerControl.GiveMoney(curPlayer, moneyType, moneyValue, "HeroBookAct", notifyAward=True)
    Sync_HeroInfo(curPlayer, [heroID])
    RefreshLordAttr(curPlayer)
    return
def __doHeroBookStarLVUP(curPlayer, heroID):
    ## 图鉴星级升级
    playerID = curPlayer.GetPlayerID()
    if not GetHeroBookInitState(curPlayer, heroID):
        GameWorld.DebugLog("该武将图鉴未激活! heroID=%s" % heroID, playerID)
        return
    bookStar = GetHeroBookStarLV(curPlayer, heroID)
    bookStarH = GetHeroBookStarLVH(curPlayer, heroID)
    if bookStar >= bookStarH:
        GameWorld.DebugLog("该武将图鉴星级已达当前英雄最高星级! heroID=%s,bookStar=%s >= %s" % (heroID, bookStar, bookStarH), playerID)
        return
    GameWorld.DebugLog("武将图鉴星级升级! heroID=%s,bookStar=%s,bookStarH=%s" % (heroID, bookStar, bookStarH), playerID)
    SetHeroBookStarLV(curPlayer, heroID, bookStar + 1)
    Sync_HeroInfo(curPlayer, [heroID])
    RefreshLordAttr(curPlayer)
    return
def __doHeroBookBreakLVUP(curPlayer, heroID):
    ## 图鉴突破升级
    playerID = curPlayer.GetPlayerID()
    if not GetHeroBookInitState(curPlayer, heroID):
        GameWorld.DebugLog("该武将图鉴未激活! heroID=%s" % heroID, playerID)
        return
    bookBreakLV = GetHeroBookBreakLV(curPlayer, heroID)
    bookBreakLVH = GetHeroBookBreakLVH(curPlayer, heroID)
    if bookBreakLV >= bookBreakLVH:
        GameWorld.DebugLog("该武将图鉴突破等级已达当前英雄最高突破等级! heroID=%s,bookBreakLV=%s >= %s" % (heroID, bookBreakLV, bookBreakLVH), playerID)
        return
    GameWorld.DebugLog("武将图鉴突破升级! heroID=%s,bookBreakLV=%s,bookBreakLVH=%s" % (heroID, bookBreakLV, bookBreakLVH), playerID)
    SetHeroBookBreakLV(curPlayer, heroID, bookBreakLV + 1)
    Sync_HeroInfo(curPlayer, [heroID])
    RefreshLordAttr(curPlayer)
    return
#// B2 38 武将锁定 #tagCSHeroLock
#
#struct    tagCSHeroLock
#{
#    tagHead        Head;
#    WORD        ItemIndex;    //武将物品所在武将背包位置索引
#    BYTE        IsLock;        //0-解锁;1-锁定
#};
def OnHeroLock(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    itemIndex = clientData.ItemIndex
    isLock = clientData.IsLock
    heroItem = GetHeroItem(curPlayer, itemIndex)
    if not heroItem:
        return
    heroItem.SetIsLocked(1 if isLock else 0)
    return
#// B2 39 武将重生 #tagCSHeroRebirth
#
#struct    tagCSHeroRebirth
#{
#    tagHead        Head;
#    WORD        ItemIndex;    //武将物品所在武将背包位置索引
#};
def OnHeroRebirth(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    itemIndex = clientData.ItemIndex
    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 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()
    qualityAwakeIpyData = IpyGameDataPY.GetIpyGameDataNotLog("HeroQualityAwake", quality, awakeLV)
    awakeCostMoney = qualityAwakeIpyData.GetRebirthCostMoney() if qualityAwakeIpyData else 0
    moneyType = IpyGameDataPY.GetFuncCfg("HeroRebirth", 1)
    lvCostMoney = int(eval(IpyGameDataPY.GetFuncCompileCfg("HeroRebirth", 3)))
    costMoneyTotal = lvCostMoney + awakeCostMoney
    GameWorld.DebugLog("武将重生: itemIndex=%s,heroID=%s,quality=%s,heroLV=%s,breakLV=%s,awakeLV=%s,costMoneyTotal=%s(%s+%s)"
                       % (itemIndex, heroID, quality, heroLV, breakLV, awakeLV, costMoneyTotal, lvCostMoney, awakeCostMoney))
    if moneyType and costMoneyTotal and not PlayerControl.HaveMoney(curPlayer, moneyType, costMoneyTotal):
        return
    # 验证通过,可以重生
    ratio = IpyGameDataPY.GetFuncCfg("HeroRebirth", 4)
    returnItemDict = {}
    __calcHeroLVReturnitem(quality, heroLV, returnItemDict, ratio)
    __calcHeroBreakReturnitem(quality, breakLV, returnItemDict, ratio)
    __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)
    heroItem.Sync_Item()
    if returnItemDict:
        returnItemList = [[k, v] for k, v in returnItemDict.items()]
        ItemControler.GivePlayerItemOrMail(curPlayer, returnItemList, event=["HeroRebirth", False, {}])
    if awakeLV:
        rebirthCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroAwakeRebirthCnt)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HeroAwakeRebirthCnt, rebirthCnt + 1)
        Sync_PlayerHeroInfo(curPlayer)
    PlayerOnline.GetOnlinePlayer(curPlayer).OnHeroItemUpate([itemIndex])
    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
        costItemInfo = qualityBreakIpyData.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,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 = {}
    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()
        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)
        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()]
        ItemControler.GivePlayerItemOrMail(curPlayer, returnItemList, event=["HeroDismiss", False, {}])
    return
#// B4 12 战斗阵容保存 #tagCSHeroLineupSave
#
#struct    tagCSHeroLineupPos
#{
#    WORD        ItemIndex;    //武将物品所在武将背包位置索引
#    BYTE        PosNum;        //1~n上阵位置编号  
#};
#
#struct    tagCSHeroBattlePosSave
#struct    tagCSHeroLineupSave
#{
#    tagHead        Head;
#    BYTE        FuncType;    //布阵功能类型:0-默认主阵型;其他待扩展,如某个活动的防守阵型
#    BYTE        LineupID;        //阵容ID:1-主阵容;其他待扩展,如某个防守阵容
#    BYTE        ShapeType;    //本阵容阵型,0为默认阵型,可扩展不同的阵型
#    BYTE        PosCnt;
#    tagCSHeroBattlePos    HeroPosList[PosCnt];    // 保存的阵型,只要发送最终的阵型武将位置即可
#    tagCSHeroLineupPos    HeroPosList[PosCnt];    // 保存的阵容,只发送最终的阵容武将位置即可
#};
def OnHeroBattlePosSave(index, clientData, tick):
def OnHeroLineupSave(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    funcType = clientData.FuncType
    lineupID = clientData.LineupID
    shapeType = clientData.ShapeType
    heroPosList = clientData.HeroPosList
    
    heroPosDict = {}
@@ -837,53 +1355,119 @@
        posNum = posInfo.PosNum
        itemIndex = posInfo.ItemIndex
        if itemIndex in indexList:
            # 单英雄只能一个位置,一个位置只能对应唯一英雄单位
            # 单武将只能一个位置,一个位置只能对应唯一武将单位
            continue
        indexList.append(itemIndex)
        heroPosDict[posNum] = itemIndex
        
    # 主阵型
    if funcType == 0:
        MainBattlePosSave(curPlayer, heroPosDict)
    # 其他待扩展
    elif funcType == 1:
        pass
    if lineupID not in ShareDefine.LineupList:
        GameWorld.DebugLog("不存在该阵容,无法保存! lineupID=%s" % lineupID)
        return
    
    return
def MainBattlePosSave(curPlayer, heroPosDict):
    GameWorld.DebugLog("保留主战斗阵型: %s" % heroPosDict, curPlayer.GetPlayerID())
    GameWorld.DebugLog("保存阵容: lineupID=%s, %s" % (lineupID, heroPosDict), curPlayer.GetPlayerID())
    curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
    # 直接重置旧阵型
    delCount = 0
    syncItemDict = {}
    for index in range(curPack.GetCount()):
        heroItem = curPack.GetAt(index)
        if not heroItem or heroItem.IsEmpty():
            continue
        if not heroItem.GetUserAttr(ShareDefine.Def_IudetHeroPosNum):
        lineupCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroLineup)
        if not lineupCount:
            continue
        item = heroItem.GetItem()
        item.ClearUserAttr(ShareDefine.Def_IudetHeroPosNum)
        for lpIndex in range(lineupCount)[::-1]:
            lineupValue = item.GetUserAttrByIndex(ShareDefine.Def_IudetHeroLineup, lpIndex)
            if GetLineupValue(lineupValue)[0] != lineupID:
                continue
            item.DelUserAttr(ShareDefine.Def_IudetHeroLineup, lineupValue)
            delCount += 1
            if delCount >= ShareDefine.LineupObjMax:
                break
            syncItemDict[index] = heroItem
    # 更新新阵型
    heroIDList = []
    heroItemDict = {}
    for posNum, itemIndex in heroPosDict.items():
        if itemIndex < 0 or itemIndex >= curPack.GetCount():
            continue
        heroItem = curPack.GetAt(itemIndex)
        if not heroItem or heroItem.IsEmpty():
            continue
        itemID = heroItem.GetItemTypeID()
        if itemID in heroIDList:
            GameWorld.DebugLog("同个武将只能上阵一个! itemIndex=%s,itemID=%s" % (itemIndex, itemID))
            continue
        heroIDList.append(itemID)
        item = heroItem.GetItem()
        item.SetUserAttr(ShareDefine.Def_IudetHeroPosNum, posNum)
        lineupValue = ComLineupValue(lineupID, shapeType, posNum)
        item.AddUserAttr(ShareDefine.Def_IudetHeroLineup, lineupValue)
        if itemIndex not in syncItemDict:
            syncItemDict[itemIndex] = heroItem
        heroItemDict[itemIndex] = posNum
        
    ResetHeroPack(curPlayer)
    # 主阵容修改时重整背包,约定所有背包由前端自行排序
    #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)
    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 RefreshLordAttr(curPlayer):
    ## 刷新主公属性
    CalcHeroAddAttr(curPlayer)
    PlayerOnline.GetOnlinePlayer(curPlayer).RefreshRoleAttr()
    return
def CalcHeroAddAttr(curPlayer):
    ## 计算武将对主公增加的属性
    
    heroBookAttrDict = {}
    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):
            # 图鉴未激活
            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
    GameWorld.DebugLog("武将图鉴属性: %s" % heroBookAttrDict, playerID)
    PlayerOnline.GetOnlinePlayer(curPlayer).SetCalcAttr(ChConfig.Def_CalcAttr_HeroBook, heroBookAttrDict)
    return
def Sync_HeroInfo(curPlayer, heroIDList=None):
    if heroIDList != None:
        syncHeroIDList = heroIDList
@@ -902,15 +1486,18 @@
        heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
        if not heroIpyData:
            continue
        heroIndex = heroIpyData.GetHeroIndex()
        heroActState = GameWorld.GetDictValueByBit(curPlayer, ChConfig.Def_PDict_HeroActState, heroIndex)
        if not heroActState and heroIDList == None:
        if heroIDList == None and not curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBook % heroID) \
            and not curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HeroBookH % heroID):
            continue
        
        hero = ChPyNetSendPack.tagSCHero()
        hero.HeroID = heroID
        hero.IsActive = heroActState
        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.BookStarLVH = GetHeroBookStarLVH(curPlayer, heroID)
        hero.BookBreakLVH = GetHeroBookBreakLVH(curPlayer, heroID)
        syncInfoList.append(hero)
        
    if not syncInfoList:
@@ -921,3 +1508,46 @@
    clientPack.HeroCnt = len(clientPack.HeroInfoList)    
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
def Sync_Lineup(curPlayer, lineupID=None):
    if lineupID:
        syncIDList = [lineupID]
    else:
        syncIDList = ShareDefine.LineupList
    lineupList = []
    olPlayer = PlayerOnline.GetOnlinePlayer(curPlayer)
    for lineupID in syncIDList:
        lineup = olPlayer.GetLineup(lineupID)
        if not lineup:
            continue
        posNumItemIndexDict = {v:k for k, v in lineup.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)
    if not lineupList:
        return
    clientPack = ChPyNetSendPack.tagSCLineupInfo()
    clientPack.LineupList = lineupList
    clientPack.LineupCnt = len(clientPack.LineupList)
    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