ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
@@ -35,6 +35,7 @@
import SkillCommon
import AttackCommon
import BattleObj
import TurnPassive
import TurnSkill
import TurnBuff
import ObjPool
@@ -43,6 +44,7 @@
import time
import json
TimelineSet = 10000 # 单回合最大时间轴
PosNumMax = 10 # 最大站位编号
ActionNumStart = -1 # 起始行动位置编号,一般是从1开始,如果有加主公、红颜等则扣除相应位置值,如从0或-1开始
@@ -67,6 +69,8 @@
        self.shapeType = 0 # 阵型
        self.fightPower = 0 # 阵容总战力
        self.posObjIDDict = {} # 站位对应战斗实体 {站位编号:batObjID, ...}, 站位编号小于0为非主战单位,如主公、红颜等
        self.lingshouObjIDDict = {} # 灵兽战斗单位 {位置编号:batObjID, ...}
        self.beautyObjIDDict = {} # 红颜战斗单位 {位置编号:batObjID, ...}
        self.actionNum = ActionNumStart # 行动位置,从1开始
        return
    
@@ -86,12 +90,16 @@
    
    def clearLineup(self):
        ## 清除阵容
        if not self.posObjIDDict:
            return
        batObjMgr = BattleObj.GetBatObjMgr()
        for objID in self.posObjIDDict.values():
            batObjMgr.delBatObj(objID)
        for objID in self.lingshouObjIDDict.values():
            batObjMgr.delBatObj(objID)
        for objID in self.beautyObjIDDict.values():
            batObjMgr.delBatObj(objID)
        self.posObjIDDict = {}
        self.lingshouObjIDDict = {}
        self.beautyObjIDDict = {}
        self.fightPower = 0
        return
    
@@ -151,6 +159,7 @@
        self.factionDict = {} # 战斗阵营 {faction:BatFaction, ...},一般是只有两个阵营,faction为1或2,每个阵营支持多个阵容
        self.actionSortList = [] # 阵容行动顺序 [[faction, num], ...]
        self.actionIndex = 0 # 行动顺序索引
        self.timeline = 0 # 时间轴节点  turnNum*1000+actionIndex*100++actionNum
        
        self.startTime = 0 # 开始时间戳,支持毫秒小数
        self.costTime = 0 # 单场战斗总耗时,支持毫秒小数
@@ -197,18 +206,35 @@
        for batFaction in self.factionDict.values():
            faction = batFaction.faction
            for num, batLineup in batFaction.lineupDict.items():
                isPlayer = 1 if batLineup.ownerID else 0 # 玩家阵容优先攻击
                fightPower = batLineup.fightPower
                sortValue = -(faction * 10 + num)
                sortList.append([fightPower, sortValue, faction, num])
                sortList.append([isPlayer, fightPower, sortValue, faction, num])
        sortList.sort(reverse=True) # 战力高的先手
        
        self.actionIndex = 0
        self.actionSortList = []
        for _, _, faction, num in sortList:
        for _, _, _, faction, num in sortList:
            self.actionSortList.append([faction, num])
            
        GameWorld.DebugLog("阵容战力排序[fp, sortV, f, n]: %s" % sortList)
        GameWorld.DebugLog("阵容战力排序[isPlayer, fp, sortV, f, n]: %s" % sortList)
        GameWorld.DebugLog("阵容行动顺序[f, n]: %s" % self.actionSortList)
        return
    def getTimeline(self): return self.timeline
    def setTimeline(self, turnNum):
        '''回合战斗的时间轴节点 ,即第几回合开始,每个回合支持9999个行动节点
        @param turnNum: 第x回合
        '''
        self.timeline = turnNum * TimelineSet + 0
        GameWorld.DebugLog("时间节点更新: %s" % self.timeline)
        OnTimelineChange(self)
        return
    def addTimeline(self):
        ## 每切换一个行动单位可视为一个行动节点,即代表单回合战斗中的某一个时间节点
        self.timeline += 1
        GameWorld.DebugLog("时间节点更新: %s" % self.timeline)
        OnTimelineChange(self)
        return
    
    def getBatFaction(self, faction=ChConfig.Def_FactionA):
@@ -241,12 +267,12 @@
                    batObj = batObjMgr.getBatObj(objID)
                    if not batObj:
                        continue
                    if batObj.GetHP() > 0:
                    if batObj.IsAlive():
                        allKilled = False
                        break
                    
            if allKilled:
                self.winFaction = ChConfig.Def_FactionA if faction == ChConfig.Def_FactionB else ChConfig.Def_FactionA
                self.winFaction = ChConfig.Def_FactionA if faction == ChConfig.Def_FactionB else ChConfig.Def_FactionB
                DoTurnFightOver(self.guid)
                return self.winFaction
            
@@ -263,6 +289,7 @@
    def startFight(self):
        ## 准备就绪,开始战斗
        self.state = FightState_Start
        self.setTimeline(1)
        self.syncInit()
        return
    
@@ -529,7 +556,7 @@
    
    batObjMgr = BattleObj.GetBatObjMgr()
    initXP = IpyGameDataPY.GetFuncCfg("AngerXP", 1)
    atkBackSkillIDList = IpyGameDataPY.GetFuncEvalCfg("ParryCfg", 3)
    atkBackSkillIDList = IpyGameDataPY.GetFuncEvalCfg("ParryCfg", 2)
    for posNumKey, heroInfo in heroDict.items():
        posNum = int(posNumKey)
        
@@ -570,6 +597,7 @@
        batObj.SetLineupPos(posNum, num)
        batObj.SetFightPower(fightPower)
        batObj.SetLV(lv)
        batObj.SetAtkDistType(atkDistType)
        if npcID:
            batObj.SetNPCID(npcID)
        elif lineupPlayerID:
@@ -587,7 +615,7 @@
            skillManager.LearnSkillByID(skillID)
            
        batLineup.posObjIDDict[posNum] = objID
        GameWorld.DebugLog("AddBatObj ID:%s,faction:%s,num=%s,posNum=%s,skill=%s" % (objID, faction, num, posNum, skillIDList))
        GameWorld.DebugLog("AddBatObj ID:%s,%s,skill=%s" % (objID, GetObjName(batObj), skillIDList))
        batObj.InitBatAttr({int(k):v for k, v in attrDict.items()}, initXP)
        
    return
@@ -847,12 +875,15 @@
        ## 关卡boss是一次性处理完的,一般不可能走到这里,这边做下防范
        return
    
    # 以下均是处理关卡小怪分段实时战斗
    EntryLogic(turnFight)
    # 小怪战斗,每次消耗1个战锤
    fightPoint = max(curPlayer.GetFightPoint(), 1) # 主线战斗消耗倍值,默认1
    if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
        GameWorld.DebugLog("回合开始时战锤不足!")
        return
    # 以下均是处理关卡小怪分段实时战斗
    EntryLogic(turnFight)
    
    # 按阵营阵容执行顺序,逐个遍历
    doCnt = 0
@@ -869,18 +900,19 @@
        if turnFight.turnStart < turnNum:
            GameWorld.DebugLog("执行行动: turnNum=%s, 回合开始" % (turnFight.turnNum))
            turnFight.syncState(FightState_Fighting)
            if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
                GameWorld.DebugLog("回合开始时战锤不足!")
                return
            #if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
            #    GameWorld.DebugLog("回合开始时战锤不足!")
            #    return
            turnFight.turnStart = turnNum
            turnFight.actionIndex = 0
            turnFight.addTimeline() # 每回合开始算一个时间节点
            for faction, num in turnFight.actionSortList:
                batFaction = turnFight.getBatFaction(faction)
                batLineup = batFaction.getBatlineup(num)
                batLineup.actionNum = ActionNumStart
                for objID in batLineup.posObjIDDict.values():
                    batObj = batObjMgr.getBatObj(objID)
                    TurnFightObjPerTurnStart(turnFight, batObj, turnNum)
                    TurnFightPerTurnBigStart(turnFight, batObj, turnNum)
                    
        if turnFight.actionIndex >= len(turnFight.actionSortList):
            turnFight.actionIndex = 0
@@ -896,10 +928,6 @@
            turnFight.actionIndex += 1
            continue
        
        if faction == ChConfig.Def_FactionA:
            if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
                GameWorld.DebugLog("战锤不足!")
                return
        GameWorld.DebugLog("执行行动: turnNum=%s,faction=%s,num=%s,actionNum=%s" % (turnFight.turnNum, faction, num, batLineup.actionNum))
        
        # 主公
@@ -913,11 +941,13 @@
        # 武将
        elif batLineup.actionNum > 0:
            for posNum in range(batLineup.actionNum, PosNumMax + 1):
                turnFight.addTimeline() # 每个武将位算一个时间节点
                batLineup.actionNum = posNum
                if posNum not in batLineup.posObjIDDict:
                    continue
                objID = batLineup.posObjIDDict[posNum]
                batObj = batObjMgr.getBatObj(objID)
                TurnFightHeroTurnStart(turnFight, batObj, turnNum)
                if not OnObjAction(turnFight, batObj):
                    continue
                
@@ -948,21 +978,24 @@
    if len(overLineupList) >= len(turnFight.actionSortList):
        GameWorld.DebugLog("执行行动: turnNum=%s, 回合结束" % (turnFight.turnNum))
        if turnFight.turnEnd < turnNum:
            if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
                GameWorld.DebugLog("回合结束时战锤不足!")
                return
            #if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
            #    GameWorld.DebugLog("回合结束时战锤不足!")
            #    return
            turnFight.turnEnd = turnNum
            turnFight.addTimeline() # 每回合结束算一个时间节点
            for faction, num in turnFight.actionSortList:
                batFaction = turnFight.getBatFaction(faction)
                batLineup = batFaction.getBatlineup(num)
                for objID in batLineup.posObjIDDict.values():
                    pass
                    batObj = batObjMgr.getBatObj(objID)
                    TurnFightPerTurnBigEnd(turnFight, batObj, turnNum)
            if turnFight.checkOverByKilled():
                return
            
        if turnNum < turnFight.turnMax:
            turnFight.turnNum += 1
            turnFight.setTimeline(turnFight.turnNum) # 回合变更,直接设置新回合时间节点
        else:
            OnTurnAllOver(turnFight.guid)
            
@@ -978,10 +1011,12 @@
    for turnNum in range(1, turnMax + 1):
        turnFight.turnNum = turnNum
        GameWorld.DebugLog("【----- 回合制战斗轮次: %s -----】" % turnNum)
        turnFight.setTimeline(turnNum)
        if curPlayer:
            turnFight.syncState(FightState_Fighting)
            
        # 回合开始
        turnFight.addTimeline() # 每回合开始算一个时间节点
        for faction, num in turnFight.actionSortList:
            GameWorld.DebugLog("回合开始逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
            batFaction = turnFight.getBatFaction(faction)
@@ -989,19 +1024,19 @@
            batLineup.actionNum = 1
            for objID in batLineup.posObjIDDict.values():
                batObj = batObjMgr.getBatObj(objID)
                TurnFightObjPerTurnStart(turnFight, batObj, turnNum)
                TurnFightPerTurnBigStart(turnFight, batObj, turnNum)
                
        # 主公
        for faction, num in turnFight.actionSortList:
            GameWorld.DebugLog("主公逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
            batFaction = turnFight.getBatFaction(faction)
            batLineup = batFaction.getBatlineup(num)
        #for faction, num in turnFight.actionSortList:
        #    GameWorld.DebugLog("主公逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
        #    batFaction = turnFight.getBatFaction(faction)
        #    batLineup = batFaction.getBatlineup(num)
            
        # 红颜
        for faction, num in turnFight.actionSortList:
            GameWorld.DebugLog("红颜逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
            batFaction = turnFight.getBatFaction(faction)
            batLineup = batFaction.getBatlineup(num)
        #for faction, num in turnFight.actionSortList:
        #    GameWorld.DebugLog("红颜逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
        #    batFaction = turnFight.getBatFaction(faction)
        #    batLineup = batFaction.getBatlineup(num)
            
        if turnFight.checkOverByKilled():
            break
@@ -1015,11 +1050,13 @@
            batFaction = turnFight.getBatFaction(faction)
            batLineup = batFaction.getBatlineup(num)
            for posNum in range(batLineup.actionNum, PosNumMax + 1):
                turnFight.addTimeline() # 每个武将位算一个时间节点
                batLineup.actionNum = posNum + 1
                if posNum not in batLineup.posObjIDDict:
                    continue
                objID = batLineup.posObjIDDict[posNum]
                batObj = batObjMgr.getBatObj(objID)
                TurnFightHeroTurnStart(turnFight, batObj, turnNum)
                if not OnObjAction(turnFight, batObj):
                    continue
                
@@ -1031,13 +1068,15 @@
                turnFight.actionIndex += 1
                
        # 回合结束
        turnFight.addTimeline() # 每回合结束算一个时间节点
        for faction, num in turnFight.actionSortList:
            GameWorld.DebugLog("回合结束逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
            batFaction = turnFight.getBatFaction(faction)
            batLineup = batFaction.getBatlineup(num)
            for objID in batLineup.posObjIDDict.values():
                pass
                batObj = batObjMgr.getBatObj(objID)
                TurnFightPerTurnBigEnd(turnFight, batObj, turnNum)
        if turnFight.checkOverByKilled():
            break
        
@@ -1061,69 +1100,132 @@
        return
    GameWorld.DebugLog("执行进场逻辑...")
    
    batObjMgr = BattleObj.GetBatObjMgr()
    for faction, num in turnFight.actionSortList:
        batFaction = turnFight.getBatFaction(faction)
        batLineup = batFaction.getBatlineup(num)
        batLineup.actionNum = ActionNumStart
        for objID in batLineup.posObjIDDict.values():
            batObj = batObjMgr.getBatObj(objID)
            TurnPassive.OnTriggerPassiveEffect(turnFight, batObj, ChConfig.TriggerWay_FightStart)
    turnFight.enterLogic = True
    return
def TurnFightObjPerTurnStart(turnFight, batObj, turnNum):
    ## 回合制战斗实例 - 每回合开始时处理
def OnTimelineChange(turnFight):
    ## 每个时间节点变化时处理
    nowTimeline = turnFight.getTimeline()
    batObjMgr = BattleObj.GetBatObjMgr()
    for batFaction in turnFight.factionDict.values():
        for batLineup in batFaction.lineupDict.values():
            for objID in batLineup.posObjIDDict.values():
                batObj = batObjMgr.getBatObj(objID)
                #GameWorld.DebugLog("OnTimelineChange! objID=%s" % (objID))
                if not batObj or not batObj.IsAlive():
                    continue
                batObj.SetDict(ChConfig.Def_Obj_Dict_TurnComboNum, 0)
                batObj.SetDict(ChConfig.Def_Obj_Dict_TurnMissNum, 0)
                batObj.SetDict(ChConfig.Def_Obj_Dict_TurnParryNum, 0)
                curID = batObj.GetID()
                skillManager = batObj.GetSkillManager()
                for index in range(0, skillManager.GetSkillCount()):
                    curSkill = skillManager.GetSkillByIndex(index)
                    if not curSkill:
                        continue
                    remainTime = curSkill.GetRemainTime()
                    if not remainTime:
                        continue
                    calcTimeline = curSkill.GetCalcTime()
                    passTurn = __calcPassturn(calcTimeline, nowTimeline, True)
                    if passTurn <= 0:
                        continue
                    skillID = curSkill.GetSkillID()
                    updRemainTime = max(0, remainTime - passTurn)
                    curSkill.SetRemainTime(updRemainTime)
                    curSkill.SetCalcTime(nowTimeline)
                    GameWorld.DebugLog("更新技能剩余回合数: curID=%s,skillID=%s,updRemainTime=%s,calcTimeline=%s,passTurn=%s"
                                       % (curID, skillID, updRemainTime, calcTimeline, passTurn))
                buffMgr = batObj.GetBuffManager()
                for index in range(buffMgr.GetBuffCount())[::-1]:
                    buff = buffMgr.GetBuffByIndex(index)
                    remainTime = buff.GetRemainTime()
                    if not remainTime:
                        # 永久buff不处理
                        #GameWorld.DebugLog("    永久buff不处理! curID=%s,index=%s,skillID=%s" % (curID, index, buff.GetSkillID()))
                        continue
                    calcTimeline = buff.GetCalcTime()
                    passTurn = __calcPassturn(calcTimeline, nowTimeline, False)
                    if passTurn <= 0:
                        #GameWorld.DebugLog("    passTurn <= 0 passTurn=%s,calcTimeline=%s,nowTimeline=%s,skillID=%s" % (passTurn, calcTimeline, nowTimeline, buff.GetSkillID()))
                        continue
                    updRemainTime = max(0, remainTime - passTurn)
                    buffID = buff.GetBuffID()
                    skillID = buff.GetSkillID()
                    GameWorld.DebugLog("更新buff剩余回合数: curID=%s,buffID=%s,skillID=%s,updRemainTime=%s,calcTimeline=%s,passTurn=%s"
                                       % (curID, buffID, skillID, updRemainTime, calcTimeline, passTurn))
                    if updRemainTime > 0:
                        buff.SetRemainTime(updRemainTime)
                        buff.SetCalcTime(nowTimeline)
                        TurnBuff.SyncBuffRefresh(turnFight, batObj, buff)
                    else:
                        TurnBuff.DoBuffDel(turnFight, batObj, buff)
    return
def __calcPassturn(calcTimeline, nowTimeline, equalOK):
    ## 计算已经过了的回合数
    # @param equalOK: 时间节点相同时是否算1回合,一般技能可以算,buff不算
    calcTurnNum = calcTimeline / TimelineSet
    calcTimeNode = calcTimeline % TimelineSet
    nowTurnNum = nowTimeline / TimelineSet
    nowTimeNode = nowTimeline % TimelineSet
    if equalOK:
        if nowTimeNode >= calcTimeNode:
            return max(0, nowTurnNum - calcTurnNum)
        return max(0, nowTurnNum - calcTurnNum - 1)
    else:
        if nowTimeNode > calcTimeNode:
            return max(0, nowTurnNum - calcTurnNum)
        return max(0, nowTurnNum - calcTurnNum - 1)
    return 0
def TurnFightPerTurnBigStart(turnFight, batObj, turnNum):
    ## 大回合开始时
    if not batObj:
        return
    
    if batObj.GetHP() <= 0:
        return
    
    curID = batObj.GetID()
    buffMgr = batObj.GetBuffManager()
    GameWorld.DebugLog("更新buff: curID=%s,buffCount=%s" % (curID, buffMgr.GetBuffCount()))
    for index in range(buffMgr.GetBuffCount())[::-1]:
        buff = buffMgr.GetBuffByIndex(index)
        curRemainTime = buff.GetRemainTime()
        if not curRemainTime:
            # 永久buff不处理
            continue
        buffID = buff.GetBuffID()
        skillID = buff.GetSkillID()
        updRemainTime = curRemainTime - 1
        GameWorld.DebugLog("    更新buff剩余回合数: buffID=%s,skillID=%s,updRemainTime=%s" % (buffID, skillID, updRemainTime))
        if updRemainTime > 0:
            buff.SetRemainTime(updRemainTime)
            TurnBuff.SyncBuffRefresh(turnFight, batObj, buff)
        else:
            TurnBuff.DoBuffDel(turnFight, batObj, buff)
#    SetTimeline(gameObj, turnNum, 0)
#    # 重置连击、反击数
#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnComboNum, 0)
#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnAtkBackNum, 0)
#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnParryNum, 0)
#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnMissNum, 0)
#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnAtkAddXPCount, 0)
#    gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnXPUseState, 0)
#    gameObj.SetDict(ChConfig.Def_PlayerKey_AttrFaintCD, 0) # 击晕CD
#
#    objType = gameObj.GetGameObjType()
#    objID = gameObj.GetID()
#    objName = GetObjName(gameObj)
#    GameWorld.DebugLog("ObjPerTurnStart: 回合%s, %s objType-ID-HP(%s-%s-%s)" % (turnNum, objName, objType, objID, GameObj.GetHP(gameObj)))
#
#    # 每回合开始减技能CD
#    skillManager = gameObj.GetSkillManager()
#    for i in range(skillManager.GetSkillCount()):
#        skill = skillManager.GetSkillByIndex(i)
#        remainTime = skill.GetRemainTime()
#        if not remainTime:
#            continue
#        skillID = skill.GetSkillID()
#        updRemainTime = max(0, remainTime - ChConfig.Def_PerTurnTick)
#        skill.SetRemainTime(updRemainTime)
#        GameWorld.DebugLog("    skillID=%s,remainTime=%s,updRemainTime=%s" % (skillID, remainTime, updRemainTime))
#
#    # 刷新定时处理的buff效果
#    SkillShell.ProcessPersistBuff(gameObj, tick)
#
#    PassiveBuffEffMng.OnPassiveSkillTrigger(gameObj, tagObj, None, ChConfig.TriggerType_TurnNum, tick)
#
#    __logGameObjAttr(gameObj)
    TurnPassive.OnTriggerPassiveEffect(turnFight, batObj, ChConfig.TriggerWay_BigTurnStart)
    return
def TurnFightPerTurnBigEnd(turnFight, batObj, turnNum):
    ## 大回合结束时
    if not batObj:
        return
    if batObj.GetHP() <= 0:
        return
    TurnPassive.OnTriggerPassiveEffect(turnFight, batObj, ChConfig.TriggerWay_BigTurnEnd)
    return
def TurnFightHeroTurnStart(turnFight, batObj, turnNum):
    ## 武将回合开始时
    if not batObj:
        return
    if batObj.GetHP() <= 0:
        return
    TurnPassive.OnTriggerPassiveEffect(turnFight, batObj, ChConfig.TriggerWay_HeroTurnStart)
    return
def AddTurnObjCureHP(curObj, srcObj, addValue, cureHP, skillID=0):
@@ -1142,19 +1244,20 @@
        
    return
def AddTurnObjHurtValue(curBatObj, tagBatObj, hurtValue, lostHP, curSkill=None, isBounce=False):
def AddTurnObjHurtValue(curBatObj, tagBatObj, hurtValue, lostHP, skillID=0, isBounce=False):
    ## 回合对象添加伤害值
    # @param isBounce: 是否反弹伤害
    if hurtValue <= 0:
        return
    curID = curBatObj.GetID()
    tagID = tagBatObj.GetID()
    skillID = curSkill.GetSkillID() if curSkill else 0
    if curID != tagID:
        updStatValue = curBatObj.StatHurtValue(hurtValue)
        GameWorld.DebugLog("        统计伤血: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,tagHP=%s,isBounce=%s" 
                       % (curID, tagID, skillID, hurtValue, lostHP, updStatValue, tagBatObj.GetHP(), isBounce))
        
        if tagBatObj:
            updStatValue = tagBatObj.StatDefValue(lostHP)
            updStatValue = tagBatObj.StatDefValue(hurtValue)
            GameWorld.DebugLog("        统计承伤: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,curHP=%s,isBounce=%s" 
                           % (tagID, curID, skillID, hurtValue, lostHP, updStatValue, tagBatObj.GetHP(), isBounce))
            
@@ -1178,8 +1281,7 @@
    objName = GetObjName(curBatObj)
    
    # 是否可行动状态判断
    canAction = True
    canAction = curBatObj.CanAction()
    if not canAction:
        GameWorld.DebugLog("★回合%s %s 当前状态不可行动!" % (turnNum, objName))
        return
@@ -1189,6 +1291,8 @@
    GameWorld.DebugLog("★回合%s %s 行动 : atk=%s,curHP=%s/%s,curXP=%s" % (turnNum, objName, atk, curHP, curBatObj.GetMaxHP(), curXP))
    turnFight.syncObjAction(turnNum, objID)
    
    TurnPassive.OnTriggerPassiveEffect(turnFight, curBatObj, ChConfig.TriggerWay_HeroActionStart)
    xpMax = IpyGameDataPY.GetFuncCfg("AngerXP", 2) 
    skillManager = curBatObj.GetSkillManager()
    useSkillList = []
@@ -1197,8 +1301,8 @@
        useSkill = skillManager.GetSkillByIndex(index)
        if not useSkill:
            continue
        if useSkill.GetFuncType() in [ChConfig.Def_SkillFuncType_AtkbackSkill]:
            #基础普攻不能主动释放,目前仅用于反击
        if useSkill.GetFuncType() not in [ChConfig.Def_SkillFuncType_TurnNormaSkill, ChConfig.Def_SkillFuncType_AngerSkill]:
            #只能主动释放普攻或怒技
            continue
        #被动技能无法使用
        if SkillCommon.isPassiveSkill(useSkill):
@@ -1211,6 +1315,9 @@
        if SkillCommon.isAngerSkill(useSkill):
            if curXP < xpMax:
                continue
            if curBatObj.IsInState(ChConfig.BatObjState_Sneer):
                GameWorld.DebugLog("嘲讽状态下,无法主动释放怒技!") # 可被动释放怒技,如怒技追击
                continue
            useCnt = -1 # xp技能优先释放
        else:
            useCnt = curBatObj.GetSkillUseCnt(skillID)
@@ -1222,209 +1329,17 @@
    for useInfo in useSkillList:
        useSkill = useInfo[-1]
        if TurnSkill.OnUseSkill(turnFight, curBatObj, useSkill):
            return True
            break
        
    return
def DoAttack(curBatObj, tagBatObj, tick, turnBattleType=ChConfig.TurnBattleType_Normal, useSkill=None):
#    curID = curBatObj.GetID()
#    npcID = curBatObj.GetNPCID()
#    objName = GetObjName(curBatObj)
#    GameWorld.DebugLog("    ● %s DoAttack: curID=%s,,turnBattleType=%s" % (objName, curID, turnBattleType))
#
#    atkOK = False
#    curBatObj.SetDict(ChConfig.Def_Obj_Dict_TurnBattleType, turnBattleType)
#
#    if turnBattleType == ChConfig.TurnBattleType_AtkBack:
#        if not tagBatObj:
#            return
#        skillManager = curBatObj.GetSkillManager()
#        for index in range(0, skillManager.GetSkillCount()):
#            skill = skillManager.GetSkillByIndex(index)
#            if not skill:
#                continue
#            if skill.GetFuncType() == ChConfig.Def_SkillFuncType_AtkbackSkill:
#                useSkill = skill
#                break
#        atkOK = SkillShell.DoLogic_UseSkill(curBatObj, tagBatObj, useSkill, tick)
#    elif turnBattleType == ChConfig.TurnBattleType_Combo:
#        if not tagBatObj:
#            return
#        atkOK = SkillShell.DoLogic_UseSkill(curBatObj, tagBatObj, useSkill, tick)
#    else:
#        curXP = GameObj.GetXP(curBatObj)
#        xpMax = IpyGameDataPY.GetFuncCfg("AngerXP", 2)
#        skillManager = curBatObj.GetSkillManager()
#        useSkillList = []
#        #GameWorld.DebugLog('skillCount=%s' % skillManager.GetSkillCount(), npcID)
#        for index in range(0, skillManager.GetSkillCount()):
#            useSkill = skillManager.GetSkillByIndex(index)
#            if not useSkill:
#                continue
#            if useSkill.GetFuncType() in [ChConfig.Def_SkillFuncType_AtkbackSkill]:
#                #基础普攻不能主动释放,目前仅用于反击
#                continue
#            #被动技能无法使用
#            if SkillCommon.isPassiveSkill(useSkill):
#                continue
#            #还在冷却时间内无法释放
#            if useSkill.GetRemainTime():
#                continue
#            skillID = useSkill.GetSkillID()
#            # 常规攻击优先xp
#            if SkillCommon.isAngerSkill(useSkill) and turnBattleType == ChConfig.TurnBattleType_Normal:
#                if curXP < xpMax:
#                    continue
#                useCnt = -1 # xp技能优先释放
#            else:
#                useCnt = curBatObj.GetDictByKey(ChConfig.Def_NPC_Dict_SkillUseCnt % skillID) # 该技能已使用次数
#            useSkillList.append([useCnt, index, skillID, useSkill])
#
#        useSkillList.sort() # 按使用次数优先升序排,使用次数低的优先判断使用
#        #GameWorld.DebugLog('    技能使用顺序 = useSkillList%s' % str(useSkillList), npcID)
#
#        for useInfo in useSkillList:
#            useSkill = useInfo[-1]
#            #skillID = useSkill.GetSkillID()
#            atkOK, tagBatObj = OnUseSkill(curBatObj, useSkill, tick)
#            if atkOK:
#                break
#
#    curBatObj.SetDict(ChConfig.Def_Obj_Dict_TurnBattleType, 0) # 无论攻击成功与否都重置战斗类型
#
#    tagID = 0
#    tagHP = 0
#    if tagObj:
#        tagID = tagObj.GetID()
#        tagHP = GameObj.GetHP(tagObj)
#    GameWorld.DebugLog('        atkOK=%s,tagID=%s,tagHP=%s' % (atkOK, tagID, tagHP), npcID)
#    if not atkOK:
#        return
#
#    if not tagObj:
#        return
#
#    if GameObj.GetFaction(curNPC) == GameObj.GetFaction(tagObj):
#        # 同阵营直接返回,不处理连击、反击
#        return True
#
#    curHP = GameObj.GetHP(curNPC)
#    tagHP = GameObj.GetHP(tagObj)
#    tagID = tagObj.GetID()
#    GameWorld.DebugLog("            curID-HP=(%s-%s),tagID-HP=(%s-%s)" % (curID, curHP, tagID, tagHP))
#    if tagHP <= 0 or curHP <= 0:
#        return True
#
#    # 反击,反击可打断连击,所以优先判断
#    if CanAtkBack(curNPC, tagObj):
#        DoAttack(tagObj, curNPC, tick, ChConfig.TurnBattleType_AtkBack)
#        return True
#
#    # 连击
#    if CanCombo(curNPC, tagObj):
#        DoAttack(curNPC, tagObj, tick, ChConfig.TurnBattleType_Combo, useSkill)
#
    TurnPassive.OnTriggerPassiveEffect(turnFight, curBatObj, ChConfig.TriggerWay_HeroActionEnd)
    return True
def CanAtkBack(atkObj, defObj):
    ## 可否反击
    posNum = atkObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo) % 100
    if posNum <= 0:
        GameWorld.DebugLog("            被非主战单位攻击时无法触发反击: atkID=%s,posNum=%s" % (atkObj.GetID(), posNum))
        return False
    atkDistType = AttackCommon.GetAtkDistType(defObj)
    if atkDistType == ChConfig.AtkDistType_Long:
        if not IpyGameDataPY.GetFuncCfg("ParryCfg", 2):
            GameWorld.DebugLog("            远程单位不可反击: defID=%s" % (defObj.GetID()))
            return False
    defAtkBackRate = GameObj.GetAtkBackRate(defObj) # 防方反击率
    atkBackNum = defObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnAtkBackNum) # 已反击次数
    if atkBackNum > 10:
        # 内置最高反击数防范
        return False
    if atkBackNum > 0:
        validPerList = IpyGameDataPY.GetFuncEvalCfg("TurnFight", 4)
        vaildPer = validPerList[atkBackNum - 1] if len(validPerList) >= atkBackNum else 0
        defAtkBackRate = int(defAtkBackRate * vaildPer / 100.0)
    atkAtkBackDefRate = GameObj.GetAtkBackDefRate(atkObj) # 攻方抵抗反击率
    atkBackRate = max(0, defAtkBackRate - atkAtkBackDefRate)
    if atkBackRate <= 0 or not GameWorld.CanHappen(atkBackRate):
        GameWorld.DebugLog("            无法反击: defID=%s,atkBackNum=%s,atkBackRate=%s=(defAtkBackRate=%s - atkAtkBackDefRate=%s)"
                           % (defObj.GetID(), atkBackNum, atkBackRate, defAtkBackRate, atkAtkBackDefRate))
        return False
    GameWorld.DebugLog("            可以反击: defID=%s,atkBackNum=%s,atkBackRate=%s=(defAtkBackRate=%s - atkAtkBackDefRate=%s)"
                       % (defObj.GetID(), atkBackNum, atkBackRate, defAtkBackRate, atkAtkBackDefRate))
    return True
def CanCombo(atkObj, defObj):
    ## 可否连击
    atkComboRate = GameObj.GetComboRate(atkObj) # 攻方连击率
    comboNum = atkObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnComboNum) # 已连击次数
    if comboNum > 10:
        # 内置最高连击数防范
        return False
    if comboNum > 0:
        validPerList = IpyGameDataPY.GetFuncEvalCfg("TurnFight", 3)
        vaildPer = validPerList[comboNum - 1] if len(validPerList) >= comboNum else 0
        atkComboRate = int(atkComboRate * vaildPer / 100.0)
    defComboReduce = GameObj.GetComboDefRate(defObj) # 防方抵抗连击率
    comboRate = max(0, atkComboRate - defComboReduce)
    if comboRate <= 0 or not GameWorld.CanHappen(comboRate):
        GameWorld.DebugLog("            无法连击: atkID=%s,comboNum=%s,comboRate=%s=(atkComboRate=%s - defComboReduce=%s)"
                           % (atkObj.GetID(), comboNum, comboRate, atkComboRate, defComboReduce))
        return False
    GameWorld.DebugLog("            可以连击: atkID=%s,comboNum=%s,comboRate=%s=(atkComboRate=%s - defComboReduce=%s)"
                       % (atkObj.GetID(), comboNum, comboRate, atkComboRate, defComboReduce))
    return True
#def GetEnemyObj(curNPC, ruleType=0):
#    ## 获取一个敌对单位,针对所有敌对阵容主战单位,优先选择对位阵容单位,仅限活着的单位
#    # @param ruleType: 选择规则,默认0任意,1-最低血量;2-最高血量
#    objID = curNPC.GetID()
#    faction = GameObj.GetFaction(curNPC)
#    posInfo = curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo)
#    num = posInfo / 100 # 阵容编号
#    posNum = posInfo % 100 # 所在阵容站位
#    turnFight = GetTurnFightMgr().getTurnFight(curNPC.GetTFGUID())
#    if not turnFight:
#        return
#
#    tagBatFaction = turnFight.getBatFaction(Def_FactionB if faction == Def_FactionA else Def_FactionA)
#
#    tagLineupNumList = [num] # 目标阵容选择顺序,优先对位阵容,再其他阵容
#    if num in tagBatFaction.lineupDict:
#        tagLineupNumList = [num]
#    for tagNum in tagBatFaction.lineupDict.keys():
#        if tagNum not in tagLineupNumList:
#            tagLineupNumList.append(tagNum)
#
#    batObjMgr = BattleObj.GetBatObjMgr()
#    for tagNum in tagLineupNumList:
#        tagLineup = tagBatFaction.getBatlineup(tagNum)
#        tagPosNumList = [posNum] # 优先对位位置,再其他位置按顺序遍历
#        pNumList = tagLineup.posObjIDDict.keys()
#        pNumList.sort()
#        for pNum in pNumList:
#            if pNum > 0 and pNum not in tagPosNumList:
#                tagPosNumList.append(pNum)
#        for pNum in tagPosNumList:
#            batObj = batObjMgr.getBatObj(objID)
#            if not batObj:
#                continue
#            if batObj.GetHP( )<= 0:
#                continue
#            return batObj
#    return
def SetObjKilled(turnFight, gameObj, killer=None, useSkill=None):
    objID = gameObj.GetID()
    GameWorld.DebugLog("        %s 回合战斗主体被击杀: curID=%s" % (GetObjName(gameObj), objID))
    gameObj.SetHP(0)
    killerObjID = killer.GetID() if killer else 0
    skillID = useSkill.GetSkillID() if useSkill else 0
    GameWorld.DebugLog("        %s 回合战斗主体被击杀: curID=%s,killerObjID=%s,skillID=%s" % (GetObjName(gameObj), objID, killerObjID, skillID))
    gameObj.SetDead()
    
    clientPack = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagMCTurnFightObjDead)
    clientPack.ObjID = objID
@@ -1462,7 +1377,7 @@
    
    turnFight.costTime = time.time() - turnFight.startTime
    winFaction = turnFight.winFaction
    GameWorld.DebugLog("--- 战斗结束处理 --- %s, winFaction=%s, costTime=%ss" % (guid, winFaction, turnFight.costTime))
    GameWorld.DebugLog("--- 战斗结束处理 ---, winFaction=%s, costTime=%ss, %s" % (winFaction, turnFight.costTime, guid))
    
    # 统计明细
    batObjMgr = BattleObj.GetBatObjMgr()
@@ -1488,7 +1403,7 @@
                atkHurt = batObj.hurtStat
                defHurt = batObj.defStat
                cureHP = batObj.cureStat
                GameWorld.DebugLog("    Pos:%s ID=%s-%s-%s,,HP=%s/%s, 输出=%s,承伤=%s,治疗=%s"
                GameWorld.DebugLog("    Pos:%s ID=%s,npcID=%s,heroID=%s,HP=%s/%s, 输出=%s,承伤=%s,治疗=%s"
                                   % (posNum, objID, npcID, heroID, batObj.GetHP(), batObj.GetMaxHP(), atkHurt, defHurt, cureHP))
                lineupStatInfo[str(posNum)] = {"ObjID":objID, "HeroID":heroID, "NPCID":npcID, "AtkHurt":atkHurt, "DefHurt":defHurt, "CureHP":cureHP}