hxp
9 天以前 2b91d1223f3d78bd2efa970478d1ed63666e131c
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py
@@ -16,13 +16,20 @@
#"""Version = 2025-07-02 17:30"""
#-------------------------------------------------------------------------------
import BattleObj
import TurnAttack
import PyGameData
import ShareDefine
import PlayerControl
import IpyGameDataPY
import FormulaControl
import PlayerPrestigeSys
import PlayerBeauty
import PlayerFamily
import PlayerHorse
import PlayerGubao
import PlayerHero
import PlayerHJG
import GameWorld
import ChConfig
import ChEquip
@@ -30,7 +37,7 @@
import time
class LineupHero():
    ## 阵容战斗武将,注意:同一个武将在不同阵容中可能属性不一样
    ## 阵容武将,注意:同一个武将在不同阵容中可能属性不一样
    
    def __init__(self):
        self.Clear()
@@ -39,6 +46,7 @@
    def Clear(self):
        self.itemIndex = 0
        self.heroID = 0
        self.skinID = 0
        self.posNum = 0
        self.heroBatAttrDict = {} # 武将的最终战斗属性字典, {attrID:value, ...}
        self.heroSkillIDList = [] # 武将拥有的技能ID列表 [skillID, ...]
@@ -55,61 +63,65 @@
        self.olPlayer = None
        self.shapeType = 0
        self.heroItemDict = {} # 阵容武将背包索引信息  {itemIndex:posNum, ...}
        self.lineupChange = False # 是否已变更阵容标记,在刷新属性后重置标记
        self.__refreshState = 0 # 刷属性标记, 0-不需要刷新了,1-需要刷新
        
        self.__freeLineupHeroObjs = [] # 释放的空闲对象[LineupHero, ...]
        self.lineupHeroDict = {} # 阵容武将 {posNum:LineupHero, ...}
        self.__lineupHeroDict = {} # 刷新阵容后的武将信息 {posNum:LineupHero, ...}
        self.fightPower = 0 # 阵容总战力
        return
    
    def UpdLineup(self, heroItemDict, shapeType=0, refreshForce=False):
    def UpdLineup(self, heroItemDict, shapeType=0, refreshForce=False, isReload=False):
        '''变更阵容时更新
        @param heroItemDict: 武将背包索引信息  {itemIndex:posNum, ...}
        @param shapeType: 阵型
        @param refreshForce: 是否强制刷属性
        '''
        if not isReload: # 非重读阵容的视为变更
            self.lineupChange = True
        self.shapeType = shapeType
        self.heroItemDict = heroItemDict
        GameWorld.DebugLog("更新阵容: lineupID=%s,%s" % (self.lineupID, heroItemDict), self.playerID)
        self.RefreshLineupAttr(refreshForce)
        if not isReload and self.olPlayer.curPlayer:
            PlayerHero.Sync_Lineup(self.olPlayer.curPlayer, self.lineupID)
        return
    def IsEmpty(self): return (not self.__lineupHeroDict or not self.heroItemDict)
    def GetPosNumList(self): return self.__lineupHeroDict.keys()
    
    def FreeLineupHero(self):
        ## 释放阵容武将对象,重新计算
        for freeObj in self.lineupHeroDict.values():
        for freeObj in self.__lineupHeroDict.values():
            if freeObj not in self.__freeLineupHeroObjs:
                self.__freeLineupHeroObjs.append(freeObj)
        self.lineupHeroDict = {}
        self.__lineupHeroDict = {}
        self.fightPower = 0
        return
    
    def GetLineupHero(self, posNum):
        lineupHero = None
        if posNum in self.lineupHeroDict:
            lineupHero = self.lineupHeroDict[posNum]
        if posNum in self.__lineupHeroDict:
            lineupHero = self.__lineupHeroDict[posNum]
        elif self.__freeLineupHeroObjs:
            lineupHero = self.__freeLineupHeroObjs.pop(0)
            lineupHero.Clear()
            self.lineupHeroDict[posNum] = lineupHero
            self.__lineupHeroDict[posNum] = lineupHero
        else:
            lineupHero = LineupHero()
            self.lineupHeroDict[posNum] = lineupHero
            self.__lineupHeroDict[posNum] = lineupHero
        return lineupHero
    
    def GetLineupHeroByID(self, heroID):
        lineupHero = None
        for posNum in self.lineupHeroDict.keys():
        for posNum in self.__lineupHeroDict.keys():
            lineupHero = self.GetLineupHero(posNum)
            if lineupHero.heroID == heroID:
                return lineupHero
        if False:
            lineupHero = LineupHero()
        return lineupHero
    def GetLineupInfo(self):
        ## 获取阵容信息,即要用到该阵容了,如战斗或者保存缓存信息等
        self.DoRefreshLineupAttr() # 取阵容时先检查
        return
    
    def SetNeedRefreshState(self):
        ## 设置需要刷属性
@@ -119,14 +131,16 @@
    def RefreshLineupAttr(self, refreshForce=False):
        self.__refreshState = 1 # 标记要刷新
        if refreshForce:
            self.DoRefreshLineupAttr()
            self.CheckRefreshLineupAttr()
        return
    
    def DoRefreshLineupAttr(self):
    def CheckRefreshLineupAttr(self):
        ## 检查刷新阵容属性
        if not self.__refreshState:
            return False
        doRefreshLineupAttr(self.olPlayer.curPlayer, self.olPlayer, self)
        self.__refreshState = 0
        doRefreshLineupAttr(self.olPlayer.curPlayer, self.olPlayer, self)
        self.lineupChange = False
        return True
    
    def CheckHeroItemUpdate(self, itemIndex):
@@ -143,15 +157,18 @@
        self.curPlayer = None
        
        # 属性、阵容
        self.calcAttrDict = {} # 非武将功能点属性统计 {calcIndex:{attrID:value, ...}, ...}
        self.lineupDict = {} # 上阵阵容 {lineupID:Lineup, ...}
        self._calcAttrDict = {} # 非武将功能点属性统计 {calcIndex:{attrID:value, ...}, ...}
        self._lineupDict = {} # 上阵阵容 {lineupID:Lineup, ...}
        self._effectiveCardDict = {} # 加成属性生效的武将卡牌信息 {heroID:[cardAddPer, itemIndex, inMain], ...}
        
        # 主线战斗
        self.mainFight = TurnAttack.MainFight(playerID)
        self._lastBatBufferInfo = [] # 最后一场战斗临时回放 ["guid", "buffer"]
        return
    
    def OnClear(self):
        self.mainFight.clear()
        self.mainFight.turnFight.exitFight()
        return
    
    def SetPlayer(self, curPlayer):
@@ -163,20 +180,32 @@
        ## 是否真的在线
        return self.curPlayer != None
    
    def GetLineup(self, lineupID):
    def GetLineup(self, lineupID, checkAttr=True):
        # @param checkAttr: 检查刷新到最新阵容属性
        lineup = None
        if lineupID in self.lineupDict:
            lineup = self.lineupDict[lineupID]
        if lineupID in self._lineupDict:
            lineup = self._lineupDict[lineupID]
        else:
            lineup = Lineup(self.playerID, lineupID)
            self.lineupDict[lineupID] = lineup
            self._lineupDict[lineupID] = lineup
        lineup.olPlayer = self
        if checkAttr:
            lineup.CheckRefreshLineupAttr()
        return lineup
    
    def GetCalcAttr(self, calcIndex): return self.calcAttrDict.get(calcIndex, {})
    def GetHeroEffectiveCard(self, heroID): return self._effectiveCardDict.get(heroID, [-1, -1, False])
    def SetHeroEffectiveCard(self, heroID, cardAddPer, itemIndex, inMain):
        ## 更新某个武将生效的卡牌信息
        self._effectiveCardDict[heroID] = [cardAddPer, itemIndex, inMain]
        self.RefreshRoleAttr()
    def SetEffectiveCardDict(self, effectiveCardDict): self._effectiveCardDict = effectiveCardDict
    def GetEffectiveCardDict(self): return self._effectiveCardDict
    def GetCalcAttr(self, calcIndex): return self._calcAttrDict.get(calcIndex, {})
    def SetCalcAttr(self, calcIndex, attrDict):
        ## 设置某个功能点计算的属性
        self.calcAttrDict[calcIndex] = attrDict
        self._calcAttrDict[calcIndex] = attrDict
        return
    
    def ReCalcAllAttr(self):
@@ -184,11 +213,13 @@
        curPlayer = self.curPlayer
        GameWorld.DebugLog("ReCalcAllAttr...", self.playerID)
        
        self.calcAttrDict = {}
        self.lineupDict = {}
        self._calcAttrDict = {}
        self._lineupDict = {}
        self._effectiveCardDict = {}
        
        doCalcAllAttr(curPlayer)
        doReloadLineup(curPlayer, self)
        reloadEffHeroCard(curPlayer, self)
        
        self.RefreshRoleAttr()
        return
@@ -200,7 +231,7 @@
        '''
        GameWorld.DebugLog("请求刷属性: refreshForce=%s" % (refreshForce), self.playerID)
        # 主公属性刷新时,所有阵容都要同步刷新
        for lineup in self.lineupDict.values():
        for lineup in self._lineupDict.values():
            lineup.SetNeedRefreshState()
            
        if refreshForce:
@@ -215,29 +246,36 @@
        
        isRefresh = False
        # 同步执行阵容属性刷新
        for lineupID, lineup in self.lineupDict.items():
        for lineupID, lineup in self._lineupDict.items():
            if not isAllLineup and lineupID != ShareDefine.Lineup_Main:
                continue
            if lineup.DoRefreshLineupAttr():
            if lineup.CheckRefreshLineupAttr():
                isRefresh = True
                
        return isRefresh
    
    def OnHeroItemUpate(self, itemIndexList):
        '''武将物品养成更新
        @param itemIndexList: 变化武将物品所在武将背包格子索引列表
    def OnHeroItemUpate(self, heroItem):
        '''武将物品变化时需要处理的逻辑
        @param heroItem: 变化武将物品
        @param return: 影响的阵容ID列表
        '''
        effLineupIDList = []
        
        for lineupID, lineup in self.lineupDict.items():
            for itemIndex in itemIndexList:
                if lineup.CheckHeroItemUpdate(itemIndex):
                    if lineupID not in effLineupIDList:
                        effLineupIDList.append(lineupID)
        GameWorld.DebugLog("武将物品养成更新索引: %s, 影响阵容:%s" % (itemIndexList, effLineupIDList), self.playerID)
        checkUpdEffHeroCard(self, heroItem) # 检查更新生效的卡牌
        itemIndex = heroItem.GetItemPlaceIndex()
        for lineupID, lineup in self._lineupDict.items():
            if lineup.CheckHeroItemUpdate(itemIndex):
                if lineupID not in effLineupIDList:
                    effLineupIDList.append(lineupID)
        GameWorld.DebugLog("武将物品变化: itemIndex=%s, 影响阵容:%s" % (itemIndex, effLineupIDList), self.playerID)
        return effLineupIDList
    def GetLastBatBuffer(self): return self._lastBatBufferInfo
    def SetLastBatBuffer(self, guid, batBuffer):
        self._lastBatBufferInfo = [guid, batBuffer]
        return
    
class OnlineMgr():
    ## 准在线玩家管理
@@ -335,6 +373,156 @@
    GetOnlinePlayer(curPlayer).SetCalcAttr(ChConfig.Def_CalcAttr_LV, lvAttrDict)
    return
def checkUpdEffHeroCard(olPlayer, heroItem, isNotify=True):
    ## 玩家武将背包卡牌变更时调用
    # @return: 加成是否变更
    if not hasattr(heroItem, "GetItemPlaceIndex"):
        return
    curPlayer = olPlayer.curPlayer
    if not curPlayer:
        return
    itemIndex = heroItem.GetItemPlaceIndex()
    heroID = heroItem.GetItemTypeID()
    curAddPer = getHeroCardAddPer(heroItem)
    effAddPer, effItemIndex, inMain = olPlayer.GetHeroEffectiveCard(heroID)
    if itemIndex == effItemIndex:
        if curAddPer == effAddPer:
            GameWorld.DebugLog("生效的卡牌不变且加成也不变,不用处理! heroID=%s,itemIndex=%s,inMain=%s,effAddPer=%s,curAddPer=%s" % (heroID, itemIndex, inMain, effAddPer, curAddPer))
            return
        olPlayer.SetHeroEffectiveCard(heroID, curAddPer, itemIndex, inMain)
        if curAddPer > effAddPer:
            GameWorld.DebugLog("生效的卡牌不变且加成提升了! heroID=%s,itemIndex=%s,inMain=%s,effAddPer=%s,curAddPer=%s" % (heroID, itemIndex, inMain, effAddPer, curAddPer))
            return
        if inMain:
            GameWorld.DebugLog("生效的卡牌效果加成降低了,但在主阵容中依旧保持生效! heroID=%s,itemIndex=%s,inMain=%s,effAddPer=%s,curAddPer=%s" % (heroID, itemIndex, inMain, effAddPer, curAddPer))
            return
        GameWorld.DebugLog("生效的卡牌效果加成降低了,未在主阵容中重新检索是否有加成更高的! heroID=%s,itemIndex=%s,inMain=%s,effAddPer=%s,curAddPer=%s"
                           % (heroID, itemIndex, inMain, effAddPer, curAddPer))
        curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
        for index in range(curPack.GetCount()):
            if index == itemIndex:
                continue
            packItem = curPack.GetAt(index)
            if not packItem or packItem.IsEmpty():
                continue
            if heroID != packItem.GetItemTypeID():
                continue
            packCardPer = getHeroCardAddPer(packItem)
            if packCardPer <= curAddPer:
                continue
            GameWorld.DebugLog("有更高加成的同名武将! heroID=%s,index=%s,packCardPer=%s > curAddPer=%s" % (heroID, index, packCardPer, curAddPer))
            checkUpdEffHeroCard(olPlayer, packItem, isNotify)
            return
        GameWorld.DebugLog("没有更高加成的同名武将,保留本卡生效! heroID=%s,itemIndex=%s,curAddPer=%s" % (heroID, itemIndex, curAddPer))
        return
    if inMain:
        GameWorld.DebugLog("没有在主阵容中且当前生效的卡牌在主阵容中不处理! heroID=%s,effItemIndex=%s,itemIndex=%s" % (heroID, effItemIndex, itemIndex))
        return
    if curAddPer <= effAddPer:
        GameWorld.DebugLog("都没有在主阵容中且不高于当前生效卡牌加成不处理! heroID=%s,itemIndex=%s,curAddPer=%s <= %s,effItemIndex=%s"
                           % (heroID, itemIndex, curAddPer, effAddPer, effItemIndex))
        return
    GameWorld.DebugLog("都没有在主阵容中且高于当前生效卡牌加成替换生效卡牌! heroID=%s,itemIndex=%s,curAddPer=%s > %s,effItemIndex=%s"
                       % (heroID, itemIndex, curAddPer, effAddPer, effItemIndex))
    olPlayer.SetHeroEffectiveCard(heroID, curAddPer, itemIndex, inMain)
    item = heroItem.GetItem()
    item.SetUserAttr(ShareDefine.Def_IudetHeroCardEffective, 1)
    isNotify and heroItem.Sync_Item()
    if effItemIndex >= 0:
        curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
        hisEffItem = curPack.GetAt(effItemIndex) if curPack.GetCount() > effItemIndex else None
        if hisEffItem and not hisEffItem.IsEmpty():
            item = hisEffItem.GetItem()
            item.SetUserAttr(ShareDefine.Def_IudetHeroCardEffective, 0)
            isNotify and hisEffItem.Sync_Item()
    return
def reloadEffHeroCard(curPlayer, olPlayer):
    ## 重新检查载入生效的卡牌,一般用于比较复杂的情况,直接重新遍历一遍,如登录时、重新保存主阵容时
    hisEffCardIndexList = [] # 历史生效的卡牌 [index, ...]
    updEffectiveCardDict = {} # 更新生效的卡牌 {heroID:[cardAddPer, itemIndex, inMain], ...}
    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()
        inMain = PlayerHero.InMainLineup(heroItem)
        cardAddPer = getHeroCardAddPer(heroItem)
        # 历史生效的
        if heroItem.GetUserAttr(ShareDefine.Def_IudetHeroCardEffective): # 是否生效的
            hisEffCardIndexList.append(index)
        # 最新生效的: 主阵容中的优先生效,非主阵容中的最高加成的生效
        if inMain:
            updEffectiveCardDict[heroID] = [cardAddPer, index, inMain]
        else:
            addPer = updEffectiveCardDict.get(heroID, [-1, -1, False])[0]
            if cardAddPer > addPer:
                updEffectiveCardDict[heroID] = [cardAddPer, index, inMain]
    # 更新生效变更的卡牌
    syncItemDict = {} # 需要同步的异常物品 {index:heroItem, ...}
    GameWorld.DebugLog("历史生效的卡牌索引: %s" % hisEffCardIndexList)
    GameWorld.DebugLog("最新生效的卡牌信息: %s" % updEffectiveCardDict)
    cardPerTotal = 0
    olPlayer.SetEffectiveCardDict(updEffectiveCardDict)
    for heroID, effInfo in updEffectiveCardDict.items():
        cardAddPer, itemIndex, inMain = effInfo
        cardPerTotal += cardAddPer
        if itemIndex in hisEffCardIndexList:
            hisEffCardIndexList.remove(itemIndex) # 不变的直接移除,剩余未移除的就是失效的
            GameWorld.DebugLog("生效的卡牌不变的: heroID=%s,itemIndex=%s,inMain=%s,cardAddPer=%s,cardPerTotal=%s" % (heroID, itemIndex, inMain, cardAddPer, cardPerTotal))
        else:
            GameWorld.DebugLog("生效的卡牌变化的: heroID=%s,itemIndex=%s,inMain=%s,cardAddPer=%s,cardPerTotal=%s" % (heroID, itemIndex, inMain, cardAddPer, cardPerTotal))
            heroItem = curPack.GetAt(itemIndex)
            item = heroItem.GetItem()
            item.SetUserAttr(ShareDefine.Def_IudetHeroCardEffective, 1)
            syncItemDict[itemIndex] = heroItem
    # 移除历史失效的卡牌
    GameWorld.DebugLog("移除失效的卡牌索引: %s" % hisEffCardIndexList)
    for itemIndex in hisEffCardIndexList:
        heroItem = curPack.GetAt(itemIndex)
        item = heroItem.GetItem()
        item.SetUserAttr(ShareDefine.Def_IudetHeroCardEffective, 0)
        syncItemDict[itemIndex] = heroItem
    # 同步变更的物品
    for syncItem in syncItemDict.values():
        syncItem.Sync_Item()
    return
def getHeroCardAddPer(heroItem):
    ## 获取该武将卡的卡牌加成
    heroID = heroItem.GetItemTypeID()
    heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
    if not heroIpyData:
        return 0
    quality = heroIpyData.GetQuality()
    qualityIpyData = IpyGameDataPY.GetIpyGameData("HeroQuality", quality)
    if not qualityIpyData:
        return 0
    heroLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroLV)
    star = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
    breakLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
    starMax = PlayerHero.GetHeroStarMax(heroItem)
    addPer = qualityIpyData.GetInitAddPer()
    addPer += qualityIpyData.GetLVAddPer() * max(0, heroLV - 1)
    addPer += qualityIpyData.GetBreakLVAddPer() * breakLV
    addPer += qualityIpyData.GetStarAddPer() * min(star, starMax)
    return addPer
def doReloadLineup(curPlayer, olPlayer):
    ## 重新载入阵容    
    loadLineupIDList = ShareDefine.LineupList
@@ -346,6 +534,7 @@
        heroItem = curPack.GetAt(index)
        if not heroItem or heroItem.IsEmpty():
            continue
        lineupCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroLineup)
        if not lineupCount:
            continue
@@ -374,18 +563,20 @@
                item.DelUserAttr(ShareDefine.Def_IudetHeroLineup, lineupValue)
            syncItemDict[index] = heroItem
            
    # 同步变更的物品
    for syncItem in syncItemDict.values():
        syncItem.Sync_Item()
        
    GameWorld.DebugLog("重载阵容: %s" % lineupDict, curPlayer.GetPlayerID())
    for lineupID, heroItemDict in lineupDict.items():
        lineup = olPlayer.GetLineup(lineupID)
        lineup = olPlayer.GetLineup(lineupID, False)
        
        # 获取其他绑定该阵容的功能,如红颜、灵兽等
        
        shapeType = lineShapeTypeDict.get(lineupID, 0)
        lineup.UpdLineup(heroItemDict, shapeType)
        lineup.UpdLineup(heroItemDict, shapeType, isReload=True)
        
    PlayerHero.Sync_Lineup(curPlayer)
    return
def doCalcAllAttr(curPlayer):
@@ -394,6 +585,11 @@
    CalcRoleBase(curPlayer)
    ChEquip.CalcRoleEquipAttr(curPlayer)
    PlayerHero.CalcHeroAddAttr(curPlayer)
    PlayerPrestigeSys.CalcOfficialRankAttr(curPlayer)
    PlayerGubao.CalcGubaoAttr(curPlayer)
    PlayerHJG.CalcHJGAttr(curPlayer)
    PlayerHorse.CalcHorseAttr(curPlayer)
    PlayerBeauty.CalcBeautyAttr(curPlayer)
    return
def doRefreshLineupAttr(curPlayer, olPlayer, lineup):
@@ -423,12 +619,10 @@
    countryHeroInfo = {} # 国家武将统计 {country:[heroID, ...], ...}
    fetterHeroInfo = {} # 阵容羁绊武将统计信息 {fetterID:[heroID, ...], ...}
    heroSelfAttrInfo = {} # 武将自身属性 {heroID:{attrID:value, ...}, ...}
    heroLVAttrInfo = {} # 武将等级属性 {heroID:{attrID:value, ...}, ...}
    heroStarTalentInfo = {} # 武将星级天赋属性 {heroID:{attrID:value, ...}, ...}
    heroBreakAttrInfo = {} # 武将突破潜能属性 {heroID:{attrID:value, ...}, ...}
    heroAwakeTalentInfo = {} # 武将觉醒天赋属性 {heroID:{attrID:value, ...}, ...}
    # 上阵卡牌【初始加成+升级加成+突破加成+吞噬加成】
    InitAddPer, LVAddPer, BreakLVAddPer, StarAddPer = 0, 0, 0, 0
    
    curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
    for itemIndex, posNum in lineup.heroItemDict.items():
@@ -447,21 +641,25 @@
            continue
        
        heroLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroLV)
        star = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
        #star = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroStar)
        breakLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroBreakLV)
        awakeLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroAwakeLV)
        skinIndex = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroSkin)
        
        InitAddPer += qualityIpyData.GetInitAddPer()
        LVAddPer += qualityIpyData.GetLVAddPer() * heroLV
        BreakLVAddPer += qualityIpyData.GetBreakLVAddPer() * breakLV
        StarAddPer += qualityIpyData.GetStarAddPer() * star
        skinID = 0
        skinIDList = heroIpyData.GetSkinIDList()
        if skinIndex < 0 or skinIndex >= len(skinIDList):
            skinID = skinIDList[skinIndex]
        elif skinIDList:
            skinID = skinIDList[0]
        lineupHero = lineup.GetLineupHero(posNum)
        #if False:
        #    lineupHero = LineupHero()
        lineupHero.itemIndex = itemIndex
        lineupHero.posNum = posNum
        lineupHero.heroID = heroID
        lineupHero.skinID = skinID
        lineupHero.heroBatAttrDict = {}
        lineupHero.heroSkillIDList = []
        lineupHero.fightPower = 0
@@ -480,26 +678,23 @@
            selfAttrDict[int(k)] = v
        heroSelfAttrInfo[heroID] = selfAttrDict
        
        # 星级天赋
        starTalentAttrDict = {}
        idCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentID)
        lvCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentIDLV)
        for aIndex in range(min(idCount, lvCount)):
            talentID = heroItem.GetUserAttrByIndex(ShareDefine.Def_IudetHeroTalentID, aIndex)
            talentLV = heroItem.GetUserAttrByIndex(ShareDefine.Def_IudetHeroTalentIDLV, aIndex)
            stIpyData = IpyGameDataPY.GetIpyGameData("HeroTalent", talentID)
            if not stIpyData:
                continue
            attrID = stIpyData.GetAttrID()
            attrValue = stIpyData.GetAttrValue() * talentLV
            starTalentAttrDict[attrID] = starTalentAttrDict.get(attrID, 0) + attrValue
        heroStarTalentInfo[heroID] = starTalentAttrDict
        # 等级属性
        heroLVAttrDict = {}
        heroLVIpyData = IpyGameDataPY.GetIpyGameData("HeroQualityLV", quality, heroLV)
        if heroLVIpyData:
            attrIDList = heroLVIpyData.GetAttrIDList()
            attrValueList = heroLVIpyData.GetAttrValueList()
            for aIndex in range(min(len(attrIDList), len(attrValueList))):
                attrID = attrIDList[aIndex]
                attrValue = attrValueList[aIndex]
                heroLVAttrDict[attrID] = heroLVAttrDict.get(attrID, 0) + attrValue
        heroLVAttrInfo[heroID] = heroLVAttrDict
        
        # 突破潜能
        breakAttrDict = {}
        awakeIpyDataList = IpyGameDataPY.GetIpyGameDataList("HeroBreak", heroID)
        if awakeIpyDataList:
            for breakIpyData in awakeIpyDataList:
        breakIpyDataList = IpyGameDataPY.GetIpyGameDataList("HeroBreak", heroID)
        if breakIpyDataList:
            for breakIpyData in breakIpyDataList:
                if breakIpyData.GetBreakLV() > breakLV:
                    break
                attrIDList = breakIpyData.GetAttrIDList()
@@ -514,12 +709,15 @@
        heroBreakAttrInfo[heroID] = breakAttrDict
        
        # 觉醒天赋
        maxUnlockSlot = IpyGameDataPY.GetFuncCfg("HeroStarTalent", 1) # 常规天赋槽个数
        awakeTalentAttrDict = {}
        awakeIpyDataList = IpyGameDataPY.GetIpyGameDataList("HeroAwake", heroID)
        awakeIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("HeroAwake", heroID)
        if awakeIpyDataList:
            for awakeIpyData in awakeIpyDataList:
                if awakeIpyData.GetAwakeLV() > awakeLV:
                    break
                unlockTalentSlot = awakeIpyData.GetUnlockTalentSlot()
                maxUnlockSlot = max(maxUnlockSlot, unlockTalentSlot)
                attrIDList = awakeIpyData.GetAttrIDList()
                attrValueList = awakeIpyData.GetAttrValueList()
                for aIndex in range(min(len(attrIDList), len(attrValueList))):
@@ -530,6 +728,21 @@
                if skillID:
                    lineupHero.heroSkillIDList.append(skillID)
        heroAwakeTalentInfo[heroID] = awakeTalentAttrDict
        # 星级天赋
        starTalentAttrDict = {}
        idCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentID)
        lvCount = heroItem.GetUserAttrCount(ShareDefine.Def_IudetHeroTalentIDLV)
        for aIndex in range(min(idCount, lvCount, maxUnlockSlot)): # 重生导致已觉醒槽位失效时属性也无效
            talentID = heroItem.GetUserAttrByIndex(ShareDefine.Def_IudetHeroTalentID, aIndex)
            talentLV = heroItem.GetUserAttrByIndex(ShareDefine.Def_IudetHeroTalentIDLV, aIndex)
            stIpyData = IpyGameDataPY.GetIpyGameData("HeroTalent", talentID)
            if not stIpyData:
                continue
            attrID = stIpyData.GetAttrID()
            attrValue = stIpyData.GetAttrValue() * talentLV
            starTalentAttrDict[attrID] = starTalentAttrDict.get(attrID, 0) + attrValue
        heroStarTalentInfo[heroID] = starTalentAttrDict
        
        # 羁绊统计
        for fetterID in heroIpyData.GetFetterIDList():
@@ -597,27 +810,49 @@
    baseAttrFormula = IpyGameDataPY.GetFuncCfg("HeroAttrFormula", 1)
    otherAttrFormula = IpyGameDataPY.GetFuncCfg("HeroAttrFormula", 2)
    fightPowerFormula = IpyGameDataPY.GetFuncCfg("HeroAttrFormula", 3)
    skillFPFormula = IpyGameDataPY.GetFuncCfg("HeroAttrFormula", 4)
    
    lvAttrDict = olPlayer.GetCalcAttr(ChConfig.Def_CalcAttr_LV)
    equipAttrDict = olPlayer.GetCalcAttr(ChConfig.Def_CalcAttr_MainEquip)
    bookAttrDict = olPlayer.GetCalcAttr(ChConfig.Def_CalcAttr_HeroBook)
    fatesAttrDict = olPlayer.GetCalcAttr(ChConfig.Def_CalcAttr_HeroFates)
    realmAttrDict = olPlayer.GetCalcAttr(ChConfig.Def_CalcAttr_Realm)
    gubaoAttrDict = olPlayer.GetCalcAttr(ChConfig.Def_CalcAttr_Gubao)
    hjgAttrDict = olPlayer.GetCalcAttr(ChConfig.Def_CalcAttr_HJG)
    horseAttrDict = olPlayer.GetCalcAttr(ChConfig.Def_CalcAttr_Horse)
    beautyAttrDict = olPlayer.GetCalcAttr(ChConfig.Def_CalcAttr_Beauty)
    GameWorld.DebugLog("    国家武将统计=%s" % countryHeroInfo, playerID)
    GameWorld.DebugLog("    羁绊武将统计=%s" % fetterHeroInfo, playerID)
    GameWorld.DebugLog("    武将自身属性=%s" % heroSelfAttrInfo, playerID)
    GameWorld.DebugLog("    武将等级属性=%s" % heroLVAttrInfo, playerID)
    GameWorld.DebugLog("    武将吞噬属性=%s" % heroStarTalentInfo, playerID)
    GameWorld.DebugLog("    武将突破潜能=%s" % heroBreakAttrInfo, playerID)
    GameWorld.DebugLog("    武将觉醒天赋=%s" % heroAwakeTalentInfo, playerID)
    GameWorld.DebugLog("    武将羁绊属性=%s" % heroFetterAttrInfo, playerID)
    GameWorld.DebugLog("    阵容光环属性=%s" % lineupHaloAttrInfo, playerID)
    GameWorld.DebugLog("    阵容上阵加成=InitAddPer=%s,LVAddPer=%s,BreakLVAddPer=%s,StarAddPer=%s" % (InitAddPer, LVAddPer, BreakLVAddPer, StarAddPer), playerID)
    
    GameWorld.DebugLog("    主公等级属性=%s" % lvAttrDict, playerID)
    GameWorld.DebugLog("    主公装备属性=%s" % equipAttrDict, playerID)
    GameWorld.DebugLog("    主公图鉴属性=%s" % bookAttrDict, playerID)
    GameWorld.DebugLog("    主公宿缘属性=%s" % fatesAttrDict, playerID)
    GameWorld.DebugLog("    主公官职属性=%s" % realmAttrDict, playerID)
    GameWorld.DebugLog("    主公古宝属性=%s" % gubaoAttrDict, playerID)
    GameWorld.DebugLog("    主幻境阁属性=%s" % hjgAttrDict, playerID)
    GameWorld.DebugLog("    主公坐骑属性=%s" % horseAttrDict, playerID)
    GameWorld.DebugLog("    主公红颜属性=%s" % beautyAttrDict, playerID)
    effCardAddPer = 0
    for effInfo in olPlayer.GetEffectiveCardDict().values():
        effCardAddPer += effInfo[0]
    effCardAddPer /= 10000.0
    GameWorld.DebugLog("    主公卡牌加成=%s" % effCardAddPer, playerID)
    PlayerLV = curPlayer.GetLV()
    OfficialLV = curPlayer.GetOfficialRank()
    GameWorld.DebugLog("    PlayerLV=%s,OfficialLV=%s" % (PlayerLV, OfficialLV), playerID)
    fpRatioIpyData = IpyGameDataPY.GetIpyGameData("FightPowerRatio", OfficialLV)
    
    lineupFightPower = 0 # 阵容总战力
    InitAddPer, LVAddPer, BreakLVAddPer, StarAddPer = InitAddPer / 10000.0, LVAddPer / 10000.0, BreakLVAddPer / 10000.0, StarAddPer / 10000.0
    for heroID, selfAttrDict in heroSelfAttrInfo.items():
        lineupHero = lineup.GetLineupHeroByID(heroID)
        if not lineupHero:
@@ -625,6 +860,7 @@
        lineupHero.heroBatAttrDict = {}
        lineupHero.fightPower = 0
        
        heroLVAttrDict = heroLVAttrInfo.get(heroID, {})
        starTalentAttrDict = heroStarTalentInfo.get(heroID, {})
        breakAttrDict = heroBreakAttrInfo.get(heroID, {})
        awakeTalentAttrDict = heroAwakeTalentInfo.get(heroID, {})
@@ -637,22 +873,39 @@
            
            lvValue = lvAttrDict.get(attrID, 0)
            equipValue = equipAttrDict.get(attrID, 0)
            bookValue = bookAttrDict.get(attrID, 0)
            bookPer = bookAttrDict.get(attrPerID, 0) / 10000.0 if attrPerID else 0
            
            lineupInitAddPer, lineupLVAddPer, lineupBreakLVAddPer, lineupStarAddPer = 0, 0, 0, 0
            cardPer = 0 # 卡牌加成,仅对基础三维有用
            if attrID in ChConfig.BaseAttrIDList:
                lineupInitAddPer, lineupLVAddPer, lineupBreakLVAddPer, lineupStarAddPer = InitAddPer, LVAddPer, BreakLVAddPer, StarAddPer
                cardPer = effCardAddPer
            fatesValue = fatesAttrDict.get(attrID, 0)
            fatesPer = fatesAttrDict.get(attrPerID, 0) / 10000.0 if attrPerID else 0
            realmValue = realmAttrDict.get(attrID, 0)
            realmPer = realmAttrDict.get(attrPerID, 0) / 10000.0 if attrPerID else 0
            gubaoValue = gubaoAttrDict.get(attrID, 0)
            gubaoPer = gubaoAttrDict.get(attrPerID, 0) / 10000.0 if attrPerID else 0
            hjgValue = hjgAttrDict.get(attrID, 0)
            hjgPer = hjgAttrDict.get(attrPerID, 0) / 10000.0 if attrPerID else 0
            horseValue = horseAttrDict.get(attrID, 0)
            horsePer = horseAttrDict.get(attrPerID, 0) / 10000.0 if attrPerID else 0
            beautyValue = beautyAttrDict.get(attrID, 0)
            beautyPer = beautyAttrDict.get(attrPerID, 0) / 10000.0 if attrPerID else 0
                
            heroSelfValue, heroSelfPer = selfAttrDict.get(attrID, 0), 0 # 武将自身基值
            inheritPer = 1 # 继承比例,默认100%
            if attrID in ChConfig.AttrInheritPerDict:
                attrInheritPerID = ChConfig.AttrInheritPerDict[attrID] # 继承ID
                inheritPer = selfAttrDict.get(attrInheritPerID, 100) # 继承比例从武将自身属性中取
                inheritPer /= 100.0
                inheritPer = selfAttrDict.get(attrInheritPerID, 10000) # 继承比例从武将自身属性中取
                inheritPer /= 10000.0
                
            lineupHaloValue, lineupHaloPer = lineupHaloAttrInfo.get(attrID, 0), 0
            fetterValue, fetterPer = fetterAttrDict.get(attrID, 0), 0
            heroLVValue, heroLVPer = heroLVAttrDict.get(attrID, 0), 0
            starTalentValue, starTalentPer = starTalentAttrDict.get(attrID, 0), 0
            breakLVValue, breakLVPer = breakAttrDict.get(attrID, 0), 0
            awakeTalentValue, awakeTalentPer = awakeTalentAttrDict.get(attrID, 0), 0
@@ -660,39 +913,46 @@
                heroSelfPer = selfAttrDict.get(attrPerID, 0) / 10000.0
                lineupHaloPer = lineupHaloAttrInfo.get(attrPerID, 0) / 10000.0
                fetterPer = fetterAttrDict.get(attrPerID, 0) / 10000.0
                heroLVPer = heroLVAttrDict.get(attrPerID, 0) / 10000.0
                starTalentPer = starTalentAttrDict.get(attrPerID, 0) / 10000.0
                breakLVPer = breakAttrDict.get(attrPerID, 0) / 10000.0
                awakeTalentPer = awakeTalentAttrDict.get(attrPerID, 0) / 10000.0
                
            # 计算
            attrParamDict = {"lvValue":lvValue, "equipValue":equipValue, "bookValue":bookValue, "bookPer":bookPer,
                             "lineupInitAddPer":lineupInitAddPer, "lineupLVAddPer":lineupLVAddPer, "lineupBreakLVAddPer":lineupBreakLVAddPer, "lineupStarAddPer":lineupStarAddPer,
                             "heroSelfValue":heroSelfValue, "heroSelfPer":heroSelfPer, "inheritPer":inheritPer,
            attrParamDict = {"lvValue":lvValue, "equipValue":equipValue, "realmValue":realmValue, "realmPer":realmPer, "cardPer":cardPer,
                             "gubaoValue":gubaoValue, "gubaoPer":gubaoPer, "hjgValue":hjgValue, "hjgPer":hjgPer, "horseValue":horseValue, "horsePer":horsePer,
                             "beautyValue":beautyValue, "beautyPer":beautyPer, "fatesValue":fatesValue, "fatesPer":fatesPer,
                             "heroSelfValue":heroSelfValue, "heroSelfPer":heroSelfPer, "inheritPer":inheritPer, "heroLVValue":heroLVValue, "heroLVPer":heroLVPer,
                             "lineupHaloValue":lineupHaloValue, "lineupHaloPer":lineupHaloPer, "fetterValue":fetterValue, "fetterPer":fetterPer,
                             "starTalentValue":starTalentValue, "starTalentPer":starTalentPer, "breakLVValue":breakLVValue, "breakLVPer":breakLVPer,
                             "awakeTalentValue":awakeTalentValue, "awakeTalentPer":awakeTalentPer,
                             }
            
            if attrID in ChConfig.BaseAttrIDList:
                attrValue = FormulaControl.Eval("baseAttrFormula", baseAttrFormula, attrParamDict)
                attrValue = FormulaControl.Eval("baseAttrFormula", baseAttrFormula, attrParamDict, toInt=False, ndigits=3)
            else:
                attrValue = FormulaControl.Eval("otherAttrFormula", otherAttrFormula, attrParamDict)
                attrValue = FormulaControl.Eval("otherAttrFormula", otherAttrFormula, attrParamDict, toInt=False, ndigits=3)
            #GameWorld.DebugLog("    attrID=%s,attrValue=%s,attrParamDict=%s" % (attrID, attrValue, attrParamDict))
            
            attrIpyData = IpyGameDataPY.GetIpyGameData("PlayerAttr", attrID)
            attrName = attrIpyData.GetParameter() if attrIpyData else "%s" % attrID
            attrRatioName = "%sRatio" % attrName
            ratioValue = 0
            if attrValue and hasattr(fpRatioIpyData, "Get%s" % attrRatioName):
                ratioValue = getattr(fpRatioIpyData, "Get%s" % attrRatioName)()
            fightPowerParamDict[attrName] = attrValue
            fightPowerParamDict[attrRatioName] = ratioValue
            if attrValue:
                lineupHero.heroBatAttrDict[attrID] = attrValue
                logAttrDict["%s-%s" % (attrID, attrName)] = attrValue
        # 计算战力
        fightPower = FormulaControl.Eval("fightPowerFormula", fightPowerFormula, fightPowerParamDict)
        fightPower = FormulaControl.Eval("fightPowerFormula", fightPowerFormula, fightPowerParamDict, toInt=True)
        
        GameWorld.DebugLog("    fightPower=%s,heroSkillIDList=%s" % (fightPower, lineupHero.heroSkillIDList))
        GameWorld.DebugLog("    heroID=%s,fightPower=%s,heroSkillIDList=%s" % (heroID, fightPower, lineupHero.heroSkillIDList), playerID)
        skillTypeIDDict = {}
        for skillID in lineupHero.heroSkillIDList:
            skillData = GameWorld.GetGameData().GetSkillBySkillID(skillID)
            skillData = IpyGameDataPY.GetIpyGameData("Skill", skillID)
            if not skillData:
                continue
            skillTypeID = skillData.GetSkillTypeID()
@@ -710,8 +970,10 @@
        for skillData in skillTypeIDDict.values():
            skillID = skillData.GetSkillID()
            lineupHero.heroSkillIDList.append(skillID)
            skillFightPower += skillData.GetFightPower()
        GameWorld.DebugLog("    skillFightPower=%s,heroSkillIDList=%s" % (skillFightPower, lineupHero.heroSkillIDList))
            paramDict = {"SkillPower":skillData.GetFightPower(), "PlayerLV":PlayerLV, "OfficialLV":OfficialLV}
            sFightPower = FormulaControl.Eval("skillFPFormula", skillFPFormula, paramDict, toInt=True)
            skillFightPower += sFightPower
        GameWorld.DebugLog("    skillFightPower=%s,heroSkillIDList=%s" % (skillFightPower, lineupHero.heroSkillIDList), playerID)
        
        # 最终战力
        fightPowerTotal = fightPower + skillFightPower
@@ -725,10 +987,41 @@
    lineup.fightPower = lineupFightPower
    GameWorld.DebugLog("    阵容最终战力: lineupID=%s,lineupFightPower=%s" % (lineupID, lineupFightPower), playerID)
    
    # 更新排行榜
    # 非主线阵容不处理以下内容
    if lineupID != ShareDefine.Lineup_Main:
        return
    
    PlayerControl.SetFightPower(curPlayer, lineupFightPower)
    
    mainFightMgr = TurnAttack.GetMainFightMgr(curPlayer)
    mainTurnFight = mainFightMgr.turnFight
    # 主线战斗如果有在战斗中,实时更新
    if mainTurnFight and mainTurnFight.isInFight():
        # 如果是阵容变化的,重新开始战斗
        if lineup.lineupChange:
            GameWorld.DebugLog("主阵容变化,重新开始战斗", playerID)
            if mainTurnFight.mapID == ChConfig.Def_FBMapID_Main:
                TurnAttack.__doMainLevelWave(curPlayer, True)
        # 否则只重新设置战斗属性
        else:
            GameWorld.DebugLog("主阵容卡牌属性变更,更新战斗武将属性", playerID)
            # lineup        为卡牌的阵容,仅有阵容属性相关,没有战斗对象
            # batLineup    为卡牌阵容体现到具体战斗的战斗阵容,有具体的战斗对象
            faction, num = ChConfig.Def_FactionA, 1 # 主线战斗玩家自己默认阵营A的第1个战斗阵容
            batLineup = mainTurnFight.getBatFaction(faction).getBatlineup(num)
            batObjMgr = BattleObj.GetBatObjMgr()
            for posNum, objID in batLineup.posObjIDDict.items():
                batObj = batObjMgr.getBatObj(objID)
                if not batObj:
                    continue
                lineupHero = lineup.GetLineupHero(posNum)
                if lineupHero.heroBatAttrDict:
                    batObj.UpdInitBatAttr(lineupHero.heroBatAttrDict, lineupHero.heroSkillIDList)
    else:
        GameWorld.DebugLog("主阵容没有在战斗中,不需要处理", playerID)
    PlayerFamily.RefreshFamilyMember(curPlayer) # 更新公会
    # 更新排行榜
    return