129 【战斗】战斗系统-服务端(buff持续时间增加大回合支持;增加光环buff;优化攻击、治疗计算逻辑,甄宓、曹仁、董白平摊伤害技能支持;优化B427封包支持额外目标;优化dot、持续治疗结算逻辑及通知;)其他:
1. TurnFight 命令支持指定敌方武将阵容测试,直接在主线战斗
2. B424初始化前的血量变更0418通知屏蔽
13个文件已修改
1453 ■■■■ 已修改文件
PySysDB/PySysDBPY.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BattleObj.py 71 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py 171 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/TurnFight.py 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/PassiveTrigger/PassiveEff_5001.py 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnBuff.py 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnBuffs/BuffAtkType_1001.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnBuffs/BuffAtkType_1002.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnPassive.py 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnSkill.py 900 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PySysDB/PySysDBPY.h
@@ -115,6 +115,7 @@
    list        BuffStateLimit;    //Buff状态限制组
    BYTE        CurBuffState;    //Buff״ֵ̬
    WORD        LastTime;    //持续时间
    BYTE        LastTimeType;    //持续时间规则
    BYTE        LayerCnt;    //Buff层数
    BYTE        LayerMax;    //最大层数
    DWORD        BuffRepeat;    //Buff叠加规则
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BattleObj.py
@@ -239,11 +239,11 @@
        self._objID = 0
        self._hurtTypes = 0 # 本次伤血类型,如闪避、暴击、格挡等,通过二进制或运算得到最终值,支持多种同时出现,如暴击的同时被格挡
        self._hurtHP = 0 # 公式最终计算的伤害值,一般用于飘血
        self._realHurtHP = 0 # 真实伤血值,被承伤盾抵扣后的可伤血的值
        self._lostHP = 0 # 实际掉血值
        self._curHP = 0 # 更新血量
        self._suckHP = 0 # 吸血量
        self._bounceHP = 0 # 反弹血量
        self._hurtListEx = [] # 额外伤血列表,一般后端单技能执行多次伤害逻辑时有用,如弹射的平摊伤害 [HurtObj, ...]
        return
    
    def GetObjID(self): return self._objID
@@ -253,14 +253,12 @@
    def AddHurtType(self, hurtType):
        ## 添加伤血类型,单次伤害支持多种类型同时出现
        self._hurtTypes |= pow(2, hurtType)
        return
        return self._hurtTypes
    def HaveHurtType(self, hurtType):
        ## 判断是否存在某种伤血类型
        return self._hurtTypes & pow(2, hurtType) 
    def GetHurtHP(self): return self._hurtHP
    def SetHurtHP(self, hurtHP): self._hurtHP = hurtHP
    def GetRealHurtHP(self): return self._realHurtHP
    def SetRealHurtHP(self, realHurtHP): self._realHurtHP = realHurtHP
    def GetLostHP(self): return self._lostHP
    def SetLostHP(self, lostHP): self._lostHP = lostHP
    def GetCurHP(self): return self._curHP
@@ -269,6 +267,18 @@
    def SetSuckHP(self, suckHP): self._suckHP = suckHP
    def GetBounceHP(self): return self._bounceHP
    def SetBounceHP(self, bounceHP): self._bounceHP = bounceHP
    def ClearHurtObjEx(self):
        poolMgr = ObjPool.GetPoolMgr()
        for hurtObjEx in self._hurtListEx:
            poolMgr.release(hurtObjEx)
        self._hurtListEx = []
        return
    def AddHurtObjEx(self, tagID):
        hurtObj = ObjPool.GetPoolMgr().acquire(HurtObj)
        hurtObj.SetObjID(tagID)
        self._hurtListEx.append(hurtObj)
        return hurtObj
    def GetHurtObjListEx(self): return self._hurtListEx # 某个伤害的额外伤害列表
    
class SkillEffect():
    
@@ -342,10 +352,11 @@
    def GetBuffStateLimit(self): return self._ipyData.GetBuffStateLimit()
    def GetCurBuffState(self): return self._ipyData.GetCurBuffState()
    def GetLastTime(self): return self._ipyData.GetLastTime() # 持续时间
    def GetLastTimeType(self): return self._ipyData.GetLastTimeType() # 持续时间规则
    def GetLayerCnt(self): return self._ipyData.GetLayerCnt()
    def GetLayerMax(self): return self._ipyData.GetLayerMax()
    def GetBuffRepeat(self): return self._ipyData.GetBuffRepeat() # Buff叠加规则
    def GetDispersedLimit(self): return self._ipyData.GetDispersedLimit() # 驱散限制
    def GetDispersedLimit(self): return self._ipyData.GetDispersedLimit() or self._ipyData.GetSkillType() == ChConfig.Def_SkillType_Halo # 驱散限制
    def GetFightPower(self): return self._ipyData.GetFightPower()
    
class PyBuff():
@@ -363,6 +374,7 @@
        self._value3 = 0
        self._isCopy = 0 # 是否复制的buff
        self._effExDict = {} # 效果ID额外数值 {effID:value, ...} # 计算方式取决于本buff技能中属性效果ID的配置
        self._haloObjIDList = [] # 光环有效目标ID列表 [objID, ...],ownerID为自己时即为光源,包含光源
        return
    
    def onRelease(self):
@@ -404,6 +416,11 @@
    def GetEffectValueEx(self, effID): return self._effExDict.get(effID, 0)
    def ResetEffectValueEx(self): self._effExDict = {}
    def AddEffectValueEx(self, effID, valueEx): self._effExDict[effID] = self._effExDict.get(effID, 0) + valueEx
    def GetHaloObjIDList(self): return self._haloObjIDList
    def SetHaloObjIDList(self, haloObjIDList): self._haloObjIDList = haloObjIDList
    def AddHaloObjID(self, objID):
        if objID not in self._haloObjIDList:
            self._haloObjIDList.append(objID)
    
class BuffManager():
    ## 战斗对象buff管理器
@@ -503,6 +520,16 @@
                continue
            buffs.append(self._buffIDDict[buffID])
        return buffs
    def FindBuffBySkillID(self, skillID, ownerID=0):
        ## 查找某个技能ID的buff
        # @param ownerID: 可指定查找指定来源的buff
        buffList = self.FindBuffListBySkillID(skillID)
        if not ownerID:
            return buffList[0] if buffList else None
        for buff in buffList:
            if buff.GetOwnerID() == ownerID:
                return buff
        return
    def FindBuffByState(self, state):
        ## 查找某种buff状态的buff
        buffIDList = self._buffStateDict.get(state, [])
@@ -552,10 +579,12 @@
        self._skillData = ObjPool.GetPoolMgr().acquire(SklllData, ipyData)
        self._remainTime = 0
        self._batType = 0 # 战斗类型,普通、连击、反击、追击等
        self._tagObjList = [] # 本次技能目标列表 [BatObj, ...]
        self._tagObjList = [] # 本次技能主要目标列表 [BatObj, ...]
        self._tagObjListEx = [] # 本次技能额外目标列表,平摊伤害、溅射等 [BatObj, ...]
        self._killObjList = [] # 本次技能击杀目标列表 [BatObj, ...]
        self._effIgnoreObjIDList = [] # 额外技能效果无效的目标ID列表,一般是被闪避、免疫等
        self._hurtList = [] # 本次伤血列表,可能同一个对象有多个伤害,如弹射等 [HurtObj, ...]
        self._hurtList = [] # 主要伤血列表,可能同一个对象有多个伤害,如弹射等 [HurtObj, ...]
        self._hurtListEx = [] # 额外伤血列表,如平摊、溅射 [HurtObj, ...]
        self._bySkill = None # 由哪个技能触发的
        self._byBuff = None # 由哪个buff触发的
        self._afterLogicList = [] # 技能释放后需要处理逻辑 [[logicType, logicParams], ...]
@@ -578,6 +607,7 @@
        ## 注:有用到对象池相关对象的一定要重置,不然再回收技能对象时会连同该技能下的所有用到的对象池对象一并回收,导致后续使用对象错误
        self._batType = 0
        self._tagObjList = []
        self._tagObjListEx = []
        self._killObjList = []
        self._effIgnoreObjIDList = []
        self._bySkill = None
@@ -616,6 +646,7 @@
    def GetBuffStateLimit(self): return self._skillData.GetBuffStateLimit()
    def GetCurBuffState(self): return self._skillData.GetCurBuffState()
    def GetLastTime(self): return self._skillData.GetLastTime() # 持续时间
    def GetLastTimeType(self): return self._skillData.GetLastTimeType() # 持续时间规则
    def GetLayerCnt(self): return self._skillData.GetLayerCnt()
    def GetLayerMax(self): return self._skillData.GetLayerMax()
    def GetBuffRepeat(self): return self._skillData.GetBuffRepeat() # Buff叠加规则
@@ -631,8 +662,12 @@
    def SetBySkill(self, bySkill): self._bySkill = bySkill
    def GetByBuff(self): return self._byBuff
    def SetByBuff(self, byBuff): self._byBuff = byBuff
    def GetTagObjList(self): return self._tagObjList # 技能目标列表
    def GetTagObjList(self): return self._tagObjList # 技能主要目标列表
    def SetTagObjList(self, tagObjList): self._tagObjList = tagObjList
    def GetTagObjListEx(self): return self._tagObjListEx # 技能额外目标列表
    def AddTagObjEx(self, tagObj):
        if tagObj not in self._tagObjList and tagObj not in self._tagObjListEx:
            self._tagObjListEx.append(tagObj)
    def GetKillObjList(self): return self._killObjList # 击杀目标列表
    def SetKillObjList(self, killObjList): self._killObjList = killObjList
    def GetEffIgnoreObjIDList(self): return self._effIgnoreObjIDList # 额外技能效果无效的目标ID列表
@@ -649,16 +684,28 @@
        ## 清空伤血统计
        poolMgr = ObjPool.GetPoolMgr()
        for hurtObj in self._hurtList:
            hurtObj.ClearHurtObjEx()
            poolMgr.release(hurtObj)
        self._hurtList = []
        for hurtObjEx in self._hurtListEx:
            hurtObjEx.ClearHurtObjEx()
            poolMgr.release(hurtObjEx)
        self._hurtListEx = []
        return
    def AddHurtObj(self, tagID):
    def AddHurtObj(self, tagID, isEx=False):
        ## 添加某个伤血
        # @param isEx: 是否额外伤血
        hurtObj = ObjPool.GetPoolMgr().acquire(HurtObj)
        hurtObj.SetObjID(tagID)
        self._hurtList.append(hurtObj)
        if isEx:
            self._hurtListEx.append(hurtObj)
        else:
            self._hurtList.append(hurtObj)
        return hurtObj
    def GetHurtObjList(self): return self._hurtList
    def GetHurtObjList(self): return self._hurtList # 技能主要伤血列表
    def GetHurtObjListEx(self): return self._hurtListEx # 技能额外伤血列表
    def GetHurtObjListAll(self): return self._hurtList + self._hurtListEx # 技能所有伤血列表
    
    def __commboClear(self):
        ## 连击相关清空
@@ -817,7 +864,7 @@
        self._skillTempAttrDict = {}
        self.SetXP(initXP, False)
        self.SetHPFull(False)
        TurnBuff.RefreshBuffAttr(self)
        TurnBuff.RefreshBuffAttr(self, isInit=True)
        TurnPassive.RefreshPassive(self)
        return
    
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
@@ -48,6 +48,8 @@
import time
import json
g_gmTestFightReq = []
PosNumMax = 7 # 最大站位编号
ActionNumStart = -1 # 起始行动位置编号,一般是从1开始,如果有加主公、红颜等则扣除相应位置值,如从0或-1开始
@@ -73,6 +75,7 @@
        self.shapeType = 0 # 阵型
        self.fightPower = 0 # 阵容总战力
        self.posObjIDDict = {} # 站位对应战斗实体 {站位编号:batObjID, ...}, 站位编号小于0为非主战单位,如主公、红颜等
        self.heroObjIDDict = {} # 武将ID对应ObjID {heroID:batObjID, ...}
        self.lingshouObjIDDict = {} # 灵兽战斗单位 {位置编号:batObjID, ...}
        self.beautyObjIDDict = {} # 红颜战斗单位 {位置编号:batObjID, ...}
        self.actionNum = ActionNumStart # 行动位置,从1开始
@@ -109,6 +112,7 @@
        for objID in self.beautyObjIDDict.values():
            batObjMgr.delBatObj(objID)
        self.posObjIDDict = {}
        self.heroObjIDDict = {}
        self.lingshouObjIDDict = {}
        self.beautyObjIDDict = {}
        self.fightPower = 0
@@ -621,7 +625,10 @@
    # @param lineupID: 阵容ID
    # @param npcLV: 成长NPC等级
    # @param difficulty: 成长NPC难度系数
    # @return: 阵容全部信息json字典,前端通用格式
    # @return: 阵容全部信息json字典,前端通用格式
    lineupInfo = GetGMTestNPCLineupInfo(lineupID, strongerLV, difficulty)
    if lineupInfo:
        return lineupInfo
    ipyData = IpyGameDataPY.GetIpyGameData("NPCLineup", lineupID)
    if not ipyData:
        return {}
@@ -643,6 +650,93 @@
    lineupInfo = {"NPCLineupID":lineupID, "Hero":heroDict, "BossID":bossID, "BossPosView":bossPosView}
    return lineupInfo
def GMTestFight(curPlayer, heroIDList, isAllSkill):
    ## GM测试战斗,指定武将
    global g_gmTestFightReq
    g_gmTestFightReq = [heroIDList, isAllSkill]
    __doMainLevelWave(curPlayer, True)
    g_gmTestFightReq = []
    return
def GetGMTestNPCLineupInfo(lineupID, strongerLV=0, difficulty=0):
    ## 获取GM测试战斗阵容信息
    if not g_gmTestFightReq:
        return
    heroIDList, isAllSkill = g_gmTestFightReq
    lineupIpyData = IpyGameDataPY.GetIpyGameData("NPCLineup", lineupID)
    if not lineupIpyData:
        return
    npcDict = {}
    heroNPCIDDict = {}
    for posNum in range(1, 1 + 6):
        if not hasattr(lineupIpyData, "GetPosNPCID%s" % posNum):
            break
        npcID = getattr(lineupIpyData, "GetPosNPCID%s" % posNum)()
        if not npcID:
            continue
        battleDict = GetNPCBattleDict(lineupIpyData, npcID, strongerLV, difficulty)
        if not battleDict:
            continue
        npcDict[npcID] = battleDict
        heroID = battleDict["HeroID"]
        heroNPCIDDict[heroID] = npcID
    if not npcDict:
        return
    heroDict = {}
    # 原先阵容刚好有的直接用
    for posNum, heroID in enumerate(heroIDList, 1):
        if heroID not in heroNPCIDDict:
            continue
        npcID = heroNPCIDDict[heroID]
        heroDict[str(posNum)] = npcDict[npcID]
        heroIDList[posNum - 1] = 0
    lineupNPCID = npcDict.keys()[0]
    ipyNPCIDList = []
    ipyHeroIDList = []
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for index in xrange(ipyDataMgr.GetNPCCount()):
        ipyData = ipyDataMgr.GetNPCByIndex(index)
        heroID = ipyData.GetRelatedHeroID()
        npcID = ipyData.GetNPCID()
        ipyNPCIDList.append(npcID)
        ipyHeroIDList.append(heroID)
    lineupNPCIndex = ipyNPCIDList.index(lineupNPCID)
    # 从参考阵容先往前检索,再往后检索,还没有对应武将的NPC
    loopIndexList = range(lineupNPCIndex + 1)[::-1] + range(lineupNPCIndex + 1, ipyDataMgr.GetNPCCount())
    for index in loopIndexList:
        npcID = ipyNPCIDList[index]
        heroID = ipyHeroIDList[index]
        if heroID not in heroIDList:
            continue
        battleDict = GetNPCBattleDict(lineupIpyData, npcID, strongerLV, difficulty)
        if not battleDict:
            continue
        for posNum, posHeroID in enumerate(heroIDList, 1):
            if not posHeroID or heroID != posHeroID:
                continue
            heroIDList[posNum - 1] = 0
            heroDict[str(posNum)] = battleDict
        if heroIDList.count(0) == len(heroIDList):
            break
    if isAllSkill:
        for battleDict in heroDict.values():
            heroID = battleDict["HeroID"]
            heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID) if heroID else None
            skillIDList = GetNPCHeroSkillIDList(heroID, heroIpyData, 99999, 99999) # 默认取突破、觉醒都满级时的技能
            battleDict["SkillIDList"] = skillIDList
    lineupInfo = {"NPCLineupID":lineupID, "Hero":heroDict, "BossID":0, "BossPosView":0}
    return lineupInfo
def GetNPCBattleDict(lineupIpyData, npcID, strongerLV=0, difficulty=0):
    ## 获取NPC战斗相关字典,支持成长NPC
    # @param strongerLV: 成长等级
@@ -771,7 +865,9 @@
    @param lineupInfo: 阵容信息
    @param playerID: 发起的玩家ID,系统场次为0
    '''
    GameWorld.DebugLog("SummonLineupObjs faction:%s,num:%s,lineupInfo=%s" % (faction, num, lineupInfo), playerID)
    lineupPlayerID = lineupInfo.get("PlayerID", 0) # 阵容所属玩家ID
    npcLineupID = lineupInfo.get("NPCLineupID", 0)
    GameWorld.DebugLog("SummonLineupObjs faction:%s,num:%s,npcLineupID=%s,lineupPlayerID=%s" % (faction, num, npcLineupID, lineupPlayerID), playerID)
    if playerID:
        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        if not curPlayer:
@@ -779,7 +875,6 @@
        
    turnFight = batLineup.turnFight
    tfGUID = turnFight.guid
    lineupPlayerID = lineupInfo.get("PlayerID", 0) # 阵容所属玩家ID
    heroDict = lineupInfo.get("Hero", {})
    
    batObjMgr = BattleObj.GetBatObjMgr()
@@ -845,6 +940,7 @@
            skillManager.LearnSkillByID(skillID)
            
        batLineup.posObjIDDict[posNum] = objID
        batLineup.heroObjIDDict[heroID] = objID
        GameWorld.DebugLog("AddBatObj %s,skill=%s" % (GetObjName(batObj), skillManager.GetSkillIDList()))
        ResetObjSkill(batObj)
        
@@ -1462,7 +1558,7 @@
    
    batObjMgr = BattleObj.GetBatObjMgr()
    for faction, num in turnFight.actionSortList:
        GameWorld.DebugLog("大回合开始逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
        GameWorld.DebugLog("大回合开始逻辑: turnNum=%s,faction=%s,num=%s" % (turnNum, faction, num))
        batFaction = turnFight.getBatFaction(faction)
        batLineup = batFaction.getBatlineup(num)
        batLineup.actionNum = 1
@@ -1476,7 +1572,8 @@
            turnFight.ResetOneActionUseSkillCnt()
            batObj.SetTiming(ChConfig.TurnTiming_Before) # 重置时机到回合前
            if turnNum > 1: # 第1回合不用刷新技能
                RefreshObjSkillByTurn(batObj)
                RefreshObjSkillByBigTurn(batObj)
                RefreshObjByBigTurn(turnFight, batObj)
                
            TurnPassive.OnTriggerPassiveEffect(turnFight, batObj, ChConfig.TriggerWay_BigTurnStart)
            
@@ -1552,7 +1649,7 @@
        
    return
def RefreshObjSkillByTurn(batObj):
def RefreshObjSkillByBigTurn(batObj):
    '''按回合刷新技能:默认以大回合统一减1回合
    '''
    curID = batObj.GetID()
@@ -1576,6 +1673,31 @@
    batObj.ResetSkillTurnUseCnt() # 重置回合使用次数,放刷新CD后重置
    return
def RefreshObjByBigTurn(turnFight, batObj):
    ## 根据大回合开始刷新buff持续时间,每个大回合-1,第1回合不处理
    curID = batObj.GetID()
    buffMgr = batObj.GetBuffManager()
    for index in range(buffMgr.GetBuffCount())[::-1]:
        buff = buffMgr.GetBuffByIndex(index)
        buffID = buff.GetBuffID()
        skillID = buff.GetSkillID()
        skillData = buff.GetSkillData()
        if skillData.GetLastTimeType() != ChConfig.BuffLastTimeType_BigTurn:
            continue
        if skillData.GetSkillType() in ChConfig.Def_LstBuff_List:
            #GameWorld.DebugLog("    持续类buff由触发时机决定剩余时间! curID=%s,index=%s,skillID=%s,buffID=%s" % (curID, index, skillID, buffID))
            continue
        if skillData.GetSkillType() == ChConfig.Def_SkillType_Halo and buff.GetOwnerID() != curID:
            GameWorld.DebugLog("    光环buff非光源不处理! curID=%s,index=%s,skillID=%s,buffID=%s" % (curID, index, skillID, buffID))
            continue
        remainTime = buff.GetRemainTime()
        if remainTime <= 0:
            continue
        remainTime -= 1
        GameWorld.DebugLog("    更新buff回合: curID=%s,buffID=%s,skillID=%s,remainTime=%s" % (curID, buffID, skillID, remainTime))
        TurnBuff.SetBuffRemainTime(turnFight, batObj, buff, remainTime)
    return
def RefreshObjBuffTime(turnFight, batObj):
    '''刷新buff持续时间:以武将自身回合前、回合后处理buff持续时间
    回合前添加的buff在回合开始减1,回合后添加的buff在回合结束减1
@@ -1588,8 +1710,13 @@
        buffID = buff.GetBuffID()
        skillID = buff.GetSkillID()
        skillData = buff.GetSkillData()
        if skillData.GetLastTimeType() != ChConfig.BuffLastTimeType_Default:
            continue
        if skillData.GetSkillType() in ChConfig.Def_LstBuff_List:
            #GameWorld.DebugLog("    持续类buff由触发时机决定剩余时间! curID=%s,index=%s,skillID=%s,buffID=%s" % (curID, index, skillID, buffID))
            continue
        if skillData.GetSkillType() == ChConfig.Def_SkillType_Halo and buff.GetOwnerID() != curID:
            GameWorld.DebugLog("    光环buff非光源不处理! curID=%s,index=%s,skillID=%s,buffID=%s" % (curID, index, skillID, buffID))
            continue
        remainTime = buff.GetRemainTime()
        if remainTime <= 0:
@@ -1606,13 +1733,9 @@
            continue
        remainTime -= 1
        GameWorld.DebugLog("    更新buff回合: curID=%s,buffID=%s,skillID=%s,remainTime=%s,addTiming=%s" % (curID, buffID, skillID, remainTime, addTiming))
        if remainTime > 0:
            buff.SetRemainTime(remainTime)
            TurnBuff.SyncBuffRefresh(turnFight, batObj, buff)
        else:
            TurnBuff.DoBuffDel(turnFight, batObj, buff)
        TurnBuff.SetBuffRemainTime(turnFight, batObj, buff, remainTime)
    return
def AddTurnObjCureHP(curObj, srcObj, addValue, cureHP, skillID=0):
    ## 回合对象添加治疗值
    # @param curObj: 获得治疗的对象
@@ -1629,22 +1752,21 @@
        
    return
def AddTurnObjHurtValue(curBatObj, tagBatObj, hurtValue, lostHP, skillID=0, isBounce=False):
def AddTurnObjHurtValue(curBatObj, tagBatObj, hurtValue, lostHP, skillID=0, lostType=""):
    ## 回合对象添加伤害值
    # @param isBounce: 是否反弹伤害
    if hurtValue <= 0:
        return
    curID = curBatObj.GetID()
    tagID = tagBatObj.GetID()
    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))
        GameWorld.DebugLog("        统计输出: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,tagHP=%s,lostType=%s"
                       % (curID, tagID, skillID, hurtValue, lostHP, updStatValue, tagBatObj.GetHP(), lostType))
        
        if tagBatObj:
            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))
            GameWorld.DebugLog("        统计承伤: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,curHP=%s,lostType=%s"
                           % (tagID, curID, skillID, hurtValue, lostHP, updStatValue, tagBatObj.GetHP(), lostType))
            
    else:
        # 如换血类技能,自残的伤害不算输出
@@ -1748,6 +1870,19 @@
    # 暂时只算主线小怪
    if curPlayer and turnFight.mapID == ChConfig.Def_FBMapID_Main and gameObj.GetFaction() != ChConfig.Def_FactionA:
        GetMainFightMgr(curPlayer).killNPCCnt += 1
    # 清除光源buff
    buffMgr = gameObj.GetBuffManager()
    for index in range(buffMgr.GetBuffCount())[::-1]:
        buff = buffMgr.GetBuffByIndex(index)
        skillID = buff.GetSkillID()
        skillData = buff.GetSkillData()
        if skillData.GetSkillType() != ChConfig.Def_SkillType_Halo:
            continue
        if buff.GetOwnerID() != objID:
            continue
        GameWorld.DebugLog("删除光环buff: objID=%s,skillID=%s" % (objID, skillID))
        TurnBuff.DoBuffDel(turnFight, gameObj, buff)
    return True
def OnTurnAllOver(guid):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -915,7 +915,7 @@
(
HurtType_Fail,              # 失败 - 如概率没有触发 0
HurtType_Normal,            # 伤害 1
HurtTYpe_Recovery,          # 回血 2
HurtTYpe_Cure,              # 治疗 2
HurtType_3,
HurtType_Immune,            # 免疫 4
HurtType_Parry,             # 格挡 5
@@ -926,7 +926,8 @@
HurtType_PoisonCureHurt,    # 伤害毒奶 10
HurtType_PoisonCureSuck,    # 吸血毒奶 11
HurtType_HarmSelf,          # 自残 12
) = range(13)
HurtType_CaorenProtect,     # 本次伤害有受曹仁防护标记 13
) = range(14)
#伤害类型
(
@@ -1448,7 +1449,7 @@
   Def_SkillType_Passive      ,  #被动技能(与被动BUFF无直接关系)   7
   Def_SkillType_Revive       ,  #复活     8
   Def_SkillType_Increment    ,  #增值技能(不可清除)9  
   Def_SkillType_Aura         ,  #光环技能  10
   Def_SkillType_Halo         ,  #光环技能  10
   Def_SkillType_Equip        ,  #装备技能  11
   Def_SkillType_Area         ,  #场景技能(buff)  12
   Def_SkillType_Summon       ,  #召唤  13
@@ -1457,6 +1458,7 @@
) = range(0, 1 + 15)
# 以下废弃
Def_SkillType_Aura = 10 #光环技能,旧命名  10
(
   Def_SkillType_LstPlsBuffAtk,  #持续攻击类BUFF 15
   Def_SkillType_PassivePlsBuff,  #被动触发增益类buff 16
@@ -1532,6 +1534,10 @@
Def_BuffLayer_Add = 0   # 每次触发层级递增
Def_BuffLayer_Sub = 1   # 每次触发层级递减
# Buff时间计算规则
#【注】光环类buff默认与光源(施法者)同步,其他受光环影响的目标同步该buff持续时间,持续时间及效果由施法者决定
BuffLastTimeType_Default = 0 # 默认以获得buff时自身回合前后判断
BuffLastTimeType_BigTurn = 1 # 大回合buff,每大回合开始固定减1回合
#动作类区分标识
(
@@ -2865,7 +2871,9 @@
TurnBattleType_Pursue, # 追击 3
TurnBattleType_Enhance, # 额外技能(一般是主技能拆分为多个效果的技能) 4
TurnBattleType_Passive, # 被动触发的技能 5
) = range(6)
TurnBattleType_Dot, # 持续伤害触发 6
TurnBattleType_Cot, # 持续治疗触发 7
) = range(8)
Def_PerTurnTick = 1000 # 每回合等同于常规tick时长
@@ -2971,7 +2979,9 @@
    BatObjState_RebornLimit, # 无法复活 25
    BatObjState_26, # 脆弱 26
    BatObjState_27, # 阴咒 27
) = range(1 + 27)
    BatObjState_Zhiming, # 织命(甄宓) 28
    BatObjState_Link, # 链接(董白) 29
) = range(1 + 29)
#玩家状态定义,不能超过31个,如超过,需扩展多个key支持
Def_PlayerStateList = (
@@ -4698,6 +4708,11 @@
HeroSpecialty_SuckHP,       # 吸血 6
) = range(1, 1 + 6)
# 部分武将ID
HeroID_Zhenfu = 510013
HeroID_Caoren = 510015
HeroID_Dongbai = 540009
# 经验倍率限制类型
(
ExpRateLimitType_Recover, # 资源找回
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
@@ -40034,17 +40034,16 @@
#------------------------------------------------------
# B4 27 使用技能 #tagSCUseSkill
class  tagSCUseSkillHurt(Structure):
class  tagSCUseSkillHurtEx(Structure):
    _pack_ = 1
    _fields_ = [
                  ("ObjID", c_int),
                  ("AttackTypes", c_int),    # 飘血类型汇总,支持多种类型并存,如无视防御且暴击同时被格挡,二进制或运算最终值;0-失败;1-普通;2-回血;5-格挡;6-无视防御;7-暴击;9-闪避
                  ("ObjID", c_int),    # 额外目标,如溅射伤害、平摊伤害目标,非技能自身的主要目标
                  ("AttackTypes", c_int),    # 飘血类型汇总,支持多种类型并存,如无视防御且暴击同时被格挡,二进制或运算最终值;0-失败;1-普通;2-回血;4-免疫;5-格挡;6-无视防御;7-暴击;8-击晕;9-闪避
                  ("HurtHP", c_int),    # 飘血值,求余亿部分
                  ("HurtHPEx", c_int),    # 飘血值,整除亿部分
                  ("CurHP", c_int),    # 更新剩余血量,求余亿部分
                  ("CurHPEx", c_int),    # 更新剩余血量,整除亿部分
                  ("SuckHP", c_int),    # 本次伤害转化的吸血量
                  ("BounceHP", c_int),    # 本次伤害反弹的伤害量
                  ]
    def __init__(self):
@@ -40064,11 +40063,10 @@
        self.CurHP = 0
        self.CurHPEx = 0
        self.SuckHP = 0
        self.BounceHP = 0
        return
    def GetLength(self):
        return sizeof(tagSCUseSkillHurt)
        return sizeof(tagSCUseSkillHurtEx)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
@@ -40081,8 +40079,110 @@
                                HurtHPEx:%d,
                                CurHP:%d,
                                CurHPEx:%d,
                                SuckHP:%d
                                '''\
                                %(
                                self.ObjID,
                                self.AttackTypes,
                                self.HurtHP,
                                self.HurtHPEx,
                                self.CurHP,
                                self.CurHPEx,
                                self.SuckHP
                                )
        return DumpString
class  tagSCUseSkillHurt(Structure):
    ObjID = 0    #(DWORD ObjID)// 技能自身目标ID
    AttackTypes = 0    #(DWORD AttackTypes)// 飘血类型汇总,支持多种类型并存,如无视防御且暴击同时被格挡,二进制或运算最终值;0-失败;1-普通;2-回血;4-免疫;5-格挡;6-无视防御;7-暴击;8-击晕;9-闪避
    HurtHP = 0    #(DWORD HurtHP)// 飘血值,求余亿部分
    HurtHPEx = 0    #(DWORD HurtHPEx)// 飘血值,整除亿部分
    CurHP = 0    #(DWORD CurHP)// 更新剩余血量,求余亿部分
    CurHPEx = 0    #(DWORD CurHPEx)// 更新剩余血量,整除亿部分
    SuckHP = 0    #(DWORD SuckHP)// 本次伤害转化的吸血量
    BounceHP = 0    #(DWORD BounceHP)// 本次伤害反弹的伤害量
    HurtCountEx = 0    #(BYTE HurtCountEx)
    HurtListEx = list()    #(vector<tagSCUseSkillHurtEx> HurtListEx)// 额外伤害目标列表,仅后端多次伤害的有效,如弹射的平摊伤害
    data = None
    def __init__(self):
        self.Clear()
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        self.ObjID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.AttackTypes,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.HurtHP,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.HurtHPEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.CurHP,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.CurHPEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.SuckHP,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.BounceHP,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.HurtCountEx,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        for i in range(self.HurtCountEx):
            temHurtListEx = tagSCUseSkillHurtEx()
            _pos = temHurtListEx.ReadData(_lpData, _pos)
            self.HurtListEx.append(temHurtListEx)
        return _pos
    def Clear(self):
        self.ObjID = 0
        self.AttackTypes = 0
        self.HurtHP = 0
        self.HurtHPEx = 0
        self.CurHP = 0
        self.CurHPEx = 0
        self.SuckHP = 0
        self.BounceHP = 0
        self.HurtCountEx = 0
        self.HurtListEx = list()
        return
    def GetLength(self):
        length = 0
        length += 4
        length += 4
        length += 4
        length += 4
        length += 4
        length += 4
        length += 4
        length += 4
        length += 1
        for i in range(self.HurtCountEx):
            length += self.HurtListEx[i].GetLength()
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteDWORD(data, self.ObjID)
        data = CommFunc.WriteDWORD(data, self.AttackTypes)
        data = CommFunc.WriteDWORD(data, self.HurtHP)
        data = CommFunc.WriteDWORD(data, self.HurtHPEx)
        data = CommFunc.WriteDWORD(data, self.CurHP)
        data = CommFunc.WriteDWORD(data, self.CurHPEx)
        data = CommFunc.WriteDWORD(data, self.SuckHP)
        data = CommFunc.WriteDWORD(data, self.BounceHP)
        data = CommFunc.WriteBYTE(data, self.HurtCountEx)
        for i in range(self.HurtCountEx):
            data = CommFunc.WriteString(data, self.HurtListEx[i].GetLength(), self.HurtListEx[i].GetBuffer())
        return data
    def OutputString(self):
        DumpString = '''
                                ObjID:%d,
                                AttackTypes:%d,
                                HurtHP:%d,
                                HurtHPEx:%d,
                                CurHP:%d,
                                CurHPEx:%d,
                                SuckHP:%d,
                                BounceHP:%d
                                BounceHP:%d,
                                HurtCountEx:%d,
                                HurtListEx:%s
                                '''\
                                %(
                                self.ObjID,
@@ -40092,7 +40192,9 @@
                                self.CurHP,
                                self.CurHPEx,
                                self.SuckHP,
                                self.BounceHP
                                self.BounceHP,
                                self.HurtCountEx,
                                "..."
                                )
        return DumpString
@@ -40101,13 +40203,15 @@
    Head = tagHead()
    ObjID = 0    #(DWORD ObjID)
    PMType = 0    #(BYTE PMType)// 物法类型 0或1-物理;2-法术
    BattleType = 0    #(BYTE BattleType)// 战斗类型 0-常规;1-连击;2-反击;3-追击;4-子技能;5-被动触发的
    BattleType = 0    #(BYTE BattleType)// 战斗类型 0-常规;1-连击;2-反击;3-追击;4-子技能;5-被动触发的;6-dot结算;7-持续治疗结算
    CurHP = 0    #(DWORD CurHP)// 释放技能后剩余血量,吸血、反弹可能引起变化,求余亿部分
    CurHPEx = 0    #(DWORD CurHPEx)// 释放技能后剩余血量,吸血、反弹可能引起变化,整除亿部分
    SkillID = 0    #(DWORD SkillID)
    RelatedSkillID = 0    #(DWORD RelatedSkillID)// 关联的技能ID,一般是主技能ID或由于某个技能释放引起的
    HurtCount = 0    #(BYTE HurtCount)//伤害数目
    HurtList = list()    #(vector<tagSCUseSkillHurt> HurtList)//size = HurtCount
    HurtCount = 0    #(BYTE HurtCount)
    HurtList = list()    #(vector<tagSCUseSkillHurt> HurtList)// 主要伤害目标列表
    HurtCountEx = 0    #(BYTE HurtCountEx)
    HurtListEx = list()    #(vector<tagSCUseSkillHurtEx> HurtListEx)// 额外伤害目标列表,仅后端单次伤害的有效,如平摊或溅射伤害
    data = None
    def __init__(self):
@@ -40131,6 +40235,11 @@
            temHurtList = tagSCUseSkillHurt()
            _pos = temHurtList.ReadData(_lpData, _pos)
            self.HurtList.append(temHurtList)
        self.HurtCountEx,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        for i in range(self.HurtCountEx):
            temHurtListEx = tagSCUseSkillHurtEx()
            _pos = temHurtListEx.ReadData(_lpData, _pos)
            self.HurtListEx.append(temHurtListEx)
        return _pos
    def Clear(self):
@@ -40147,6 +40256,8 @@
        self.RelatedSkillID = 0
        self.HurtCount = 0
        self.HurtList = list()
        self.HurtCountEx = 0
        self.HurtListEx = list()
        return
    def GetLength(self):
@@ -40162,6 +40273,9 @@
        length += 1
        for i in range(self.HurtCount):
            length += self.HurtList[i].GetLength()
        length += 1
        for i in range(self.HurtCountEx):
            length += self.HurtListEx[i].GetLength()
        return length
@@ -40178,6 +40292,9 @@
        data = CommFunc.WriteBYTE(data, self.HurtCount)
        for i in range(self.HurtCount):
            data = CommFunc.WriteString(data, self.HurtList[i].GetLength(), self.HurtList[i].GetBuffer())
        data = CommFunc.WriteBYTE(data, self.HurtCountEx)
        for i in range(self.HurtCountEx):
            data = CommFunc.WriteString(data, self.HurtListEx[i].GetLength(), self.HurtListEx[i].GetBuffer())
        return data
    def OutputString(self):
@@ -40191,7 +40308,9 @@
                                SkillID:%d,
                                RelatedSkillID:%d,
                                HurtCount:%d,
                                HurtList:%s
                                HurtList:%s,
                                HurtCountEx:%d,
                                HurtListEx:%s
                                '''\
                                %(
                                self.Head.OutputString(),
@@ -40203,6 +40322,8 @@
                                self.SkillID,
                                self.RelatedSkillID,
                                self.HurtCount,
                                "...",
                                self.HurtCountEx,
                                "..."
                                )
        return DumpString
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/TurnFight.py
@@ -41,6 +41,7 @@
        GameWorld.DebugAnswer(curPlayer, "阵营: 1-左边;2-右边")
        GameWorld.DebugAnswer(curPlayer, "位置: 1~6号位")
        GameWorld.DebugAnswer(curPlayer, "属性ID: 6-攻,7-防,8-HPMax,9-HP,12-怒")
        GameWorld.DebugAnswer(curPlayer, "测试战斗: TurnFight f 是否满技能 [武将ID ...]")
        return
    
    value = msgList[0]
@@ -52,6 +53,8 @@
        __doAddBuff(curPlayer, msgList)
    elif value == "p":
        __printInfo(curPlayer, msgList)
    elif value == "f":
        __doFightTest(curPlayer, msgList)
    elif value > 0 and value != ChConfig.Def_FBMapID_Main:
        __reqTurnFight(curPlayer, msgList)
    return
@@ -230,7 +233,8 @@
            batObj = batObjMgr.getBatObj(objID)
            objName = TurnAttack.GetObjName(batObj)
            GameWorld.DebugAnswer(curPlayer, "--- %s%s" % (objName, "" if batObj.IsAlive() else " [被击杀]"))
            GameWorld.DebugAnswer(curPlayer, "HP:%s/%s, Atk:%s, Def:%s" % (batObj.GetHP(), batObj.GetMaxHP(), batObj.GetAtk(), batObj.GetDef()))
            GameWorld.DebugAnswer(curPlayer, "HP:%s/%s" % (batObj.GetHP(), batObj.GetMaxHP()))
            GameWorld.DebugAnswer(curPlayer, "攻:%s,防:%s,怒:%s" % (batObj.GetAtk(), batObj.GetDef(), batObj.GetXP()))
            attrDict = batObj.GetBatAttrDict()
            GameWorld.DebugAnswer(curPlayer, "属性:%s" % attrDict)
            skillMgr = batObj.GetSkillManager()
@@ -240,11 +244,17 @@
            GameWorld.DebugAnswer(curPlayer, "Buff: %s" % buffMgr.GetBuffCount())
            for index in range(buffMgr.GetBuffCount()):
                buff = buffMgr.GetBuffByIndex(index)
                GameWorld.DebugAnswer(curPlayer, "ID:%s,SkillID:%s,回合:%s,层:%s,V:%s"
                GameWorld.DebugAnswer(curPlayer, "ID:%s,SkillID:%s,回合:%s,层:%s,V:%s,来源:%s,光环:%s"
                                      % (buff.GetBuffID(), buff.GetSkillID(), buff.GetRemainTime(), buff.GetLayer(), 
                                         [buff.GetValue1(), buff.GetValue2(), buff.GetValue3()]
                                         [buff.GetValue1(), buff.GetValue2(), buff.GetValue3()], buff.GetOwnerID(), buff.GetHaloObjIDList()
                                         ))
                
    return
def __doFightTest(curPlayer, msgList):
    ## 测试战斗: TurnFight f 是否满技能 [位置1武将ID ...]
    isAllSkill = msgList[1] if len(msgList) > 1 else 1
    heroIDList = msgList[2:]
    TurnAttack.GMTestFight(curPlayer, heroIDList, isAllSkill)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
@@ -137,6 +137,7 @@
                        ("list", "BuffStateLimit", 0),
                        ("BYTE", "CurBuffState", 0),
                        ("WORD", "LastTime", 0),
                        ("BYTE", "LastTimeType", 0),
                        ("BYTE", "LayerCnt", 0),
                        ("BYTE", "LayerMax", 0),
                        ("DWORD", "BuffRepeat", 0),
@@ -2358,11 +2359,12 @@
    def GetBuffStateLimit(self): return self.attrTuple[32] # Buff状态限制组 list
    def GetCurBuffState(self): return self.attrTuple[33] # Buff״ֵ̬ BYTE
    def GetLastTime(self): return self.attrTuple[34] # 持续时间 WORD
    def GetLayerCnt(self): return self.attrTuple[35] # Buff层数 BYTE
    def GetLayerMax(self): return self.attrTuple[36] # 最大层数 BYTE
    def GetBuffRepeat(self): return self.attrTuple[37] # Buff叠加规则 DWORD
    def GetDispersedLimit(self): return self.attrTuple[38] # 驱散限制 BYTE
    def GetFightPower(self): return self.attrTuple[39] # 技能战斗力 DWORD
    def GetLastTimeType(self): return self.attrTuple[35] # 持续时间规则 BYTE
    def GetLayerCnt(self): return self.attrTuple[36] # Buff层数 BYTE
    def GetLayerMax(self): return self.attrTuple[37] # 最大层数 BYTE
    def GetBuffRepeat(self): return self.attrTuple[38] # Buff叠加规则 DWORD
    def GetDispersedLimit(self): return self.attrTuple[39] # 驱散限制 BYTE
    def GetFightPower(self): return self.attrTuple[40] # 技能战斗力 DWORD
# 武将表
class IPY_Hero():
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/PassiveTrigger/PassiveEff_5001.py
@@ -29,10 +29,6 @@
        return True
    
    remainTime -= 1
    if remainTime <= 0:
        TurnBuff.DoBuffDel(turnFight, batObj, effBuff)
    else:
        effBuff.SetRemainTime(remainTime)
        TurnBuff.SyncBuffRefresh(turnFight, batObj, effBuff)
    TurnBuff.SetBuffRemainTime(turnFight, batObj, effBuff, remainTime)
    return True
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnBuff.py
@@ -87,18 +87,27 @@
        buffOwner = batObj
    ownerID = buffOwner.GetID()
    
    skillType = buffSkill.GetSkillType()
    #无敌免疫持续减益buff、控制类buff
    if buffSkill.GetSkillType() in [ChConfig.Def_SkillType_LstDepBuff, ChConfig.Def_SkillType_Action] \
    if skillType in [ChConfig.Def_SkillType_LstDepBuff, ChConfig.Def_SkillType_Action] \
        and batObj.CheckInState(ChConfig.BatObjState_Wudi):
        GameWorld.DebugLog("无敌状态下免疫该buff: curID=%s,skillID=%s,ownerID=%s,relatedSkillID=%s" 
                           % (curID, skillID, ownerID, relatedSkillID))
        return
    
    #被动触发免疫控制buff
    if buffSkill.GetSkillType() == ChConfig.Def_SkillType_Action:
    if skillType == ChConfig.Def_SkillType_Action:
        if TurnPassive.GetTriggerEffectValue(turnFight, batObj, buffOwner, ChConfig.PassiveEff_ImmuneControlBuff, buffSkill):
            GameWorld.DebugLog("血量低于百分x时免疫控制buff: curID=%s,skillID=%s,ownerID=%s,relatedSkillID=%s,hp:%s/%s" 
                               % (curID, skillID, ownerID, relatedSkillID, batObj.GetHP(), batObj.GetMaxHP()))
            return
    #光环技能,默认施法者身上也必须有,时长与施法者身上的同步
    haloSrcBuff = None # 光源buff
    if skillType == ChConfig.Def_SkillType_Halo and ownerID != curID:
        haloSrcBuff = buffOwner.GetBuffManager().FindBuffBySkillID(skillID, ownerID)
        if not haloSrcBuff:
            GameWorld.ErrLog("添加光环技能时找不到光源! skillID=%s,ownerID=%s" % (skillID, ownerID))
            return
        
    buffValueList = GetAddBuffValue(turnFight, buffOwner, batObj, buffSkill)
@@ -193,7 +202,10 @@
            RefreshBuffEffect(turnFight, batObj, buff, buffSkill, buffOwner, refreshType=2)
            return buff
        
    return __addNewBuff(turnFight, batObj, buffMgr, buffSkill, buffValueList, buffOwner, bySkill, afterLogic, setLayerCnt=addLayerCnt, isSync=isSync)
    newBuff = __addNewBuff(turnFight, batObj, buffMgr, buffSkill, buffValueList, buffOwner, bySkill, afterLogic, setLayerCnt=addLayerCnt, isSync=isSync)
    if skillType == ChConfig.Def_SkillType_Halo and newBuff:
        __addHaloBuffEffObjID(curID, newBuff, skillID, ownerID, haloSrcBuff)
    return newBuff
def __addNewBuff(turnFight, batObj, buffMgr, buffSkill, buffValueList, buffOwner, bySkill=None, afterLogic=False, setLayerCnt=0, isSync=True):
    curID = batObj.GetID()
@@ -257,6 +269,60 @@
        
    return
def __addHaloBuffEffObjID(curID, newBuff, skillID, ownerID, haloSrcBuff):
    ## 添加光环buff有效的目标ID,添加光环buff时添加,不移除,由光源管理移除(光源武将死亡时删除)
    if curID == ownerID:
        newBuff.AddHaloObjID(curID) # 光源直接添加目标
        return
    haloSrcBuff.AddHaloObjID(curID) # 光源先添加新目标
    haloObjIDList = haloSrcBuff.GetHaloObjIDList()
    newBuff.SetHaloObjIDList(haloObjIDList) # 新buff直接同步设置为光源有效目标
    batObjMgr = BattleObj.GetBatObjMgr()
    for haloObjID in haloObjIDList:
        if haloObjID in [ownerID, curID]:
            continue
        haloObj = batObjMgr.getBatObj(haloObjID)
        if not haloObj:
            continue
        haloBuff = haloObj.GetBuffManager().FindBuffBySkillID(skillID, ownerID)
        if not haloBuff:
            continue
        haloBuff.SetHaloObjIDList(haloObjIDList) # 除光源及新加入的直接同步最新的有效目标
    return
def SetBuffRemainTime(turnFight, batObj, curBuff, remainTime):
    if remainTime <= 0:
        DoBuffDel(turnFight, batObj, curBuff)
        return
    buffObjID = batObj.GetID()
    ownerID = curBuff.GetOwnerID()
    skillData = curBuff.GetSkillData()
    skillID = skillData.GetSkillID()
    skillType = skillData.GetSkillType()
    curBuff.SetRemainTime(remainTime)
    SyncBuffRefresh(turnFight, batObj, curBuff)
    if skillType == ChConfig.Def_SkillType_Halo and ownerID == buffObjID:
        haloObjIDList = curBuff.GetHaloObjIDList()
        GameWorld.DebugLog("光环buff回合变更同步其他有效目标该光环: skillID=%s,ownerID=%s,haloObjIDList=%s" % (skillID, ownerID, haloObjIDList))
        batObjMgr = BattleObj.GetBatObjMgr()
        for haloObjID in haloObjIDList:
            if haloObjID == buffObjID:
                continue
            haloObj = batObjMgr.getBatObj(haloObjID)
            if not haloObj:
                continue
            haloBuff = haloObj.GetBuffManager().FindBuffBySkillID(skillID, ownerID)
            if not haloBuff:
                continue
            haloBuff.SetRemainTime(remainTime)
            SyncBuffRefresh(turnFight, haloObj, haloBuff)
    return
def DoBuffLayerChange(turnFight, batObj, curBuff, updLayer, relatedSkill=None):
    ## buff层级变更
    if updLayer > 0:
@@ -288,7 +354,10 @@
    buffObjID = batObj.GetID()
    buffMgr = batObj.GetBuffManager()
    buffID = curBuff.GetBuffID()
    ownerID = curBuff.GetOwnerID()
    skillData = curBuff.GetSkillData()
    skillID = skillData.GetSkillID()
    skillType = skillData.GetSkillType()
    
    # 先删除buff再触发其他内容,防止当前要删除的buff影响后续触发的内容,如无敌buff等,理论上触发的后续内容无敌buff不应该再生效
    curBuffState = skillData.GetCurBuffState()
@@ -326,6 +395,21 @@
        
    if isRefreshAttr:
        RefreshBuffAttr(batObj)
    if skillType == ChConfig.Def_SkillType_Halo and ownerID == buffObjID:
        haloObjIDList = curBuff.GetHaloObjIDList()
        GameWorld.DebugLog("光环buff删除同步删除其他有效目标该光环: skillID=%s,ownerID=%s,haloObjIDList=%s" % (skillID, ownerID, haloObjIDList))
        batObjMgr = BattleObj.GetBatObjMgr()
        for haloObjID in haloObjIDList:
            if haloObjID == buffObjID:
                continue
            haloObj = batObjMgr.getBatObj(haloObjID)
            if not haloObj:
                continue
            haloBuff = haloObj.GetBuffManager().FindBuffBySkillID(skillID, ownerID)
            if not haloBuff:
                continue
            DoBuffDel(turnFight, haloObj, haloBuff, relatedSkill, afterLogic, tagObj)
    return
def DoBuffDelAfterLogicOver(turnFight, buffObjID, curBuff, relatedSkill):
@@ -344,7 +428,7 @@
        callFunc(turnFight, batObj, curBuff, **kwargs)
    return
    
def RefreshBuffAttr(batObj):
def RefreshBuffAttr(batObj, isInit=False):
    ''' 刷新buff属性,如果有涉及到buff属性变更的,只能全部buff重新刷
    '''
    
@@ -438,15 +522,17 @@
    aftHP = batObj.GetHP()
    aftMaxHP = batObj.GetMaxHP()
    if aftMaxHP != befMaxHP:
        batObj.SetMaxHP(aftMaxHP, True)
        isNotify = not isInit
        batObj.SetMaxHP(aftMaxHP, isNotify)
        if befHP and aftMaxHP > befMaxHP:
            aftHP += (aftMaxHP - befMaxHP)
            batObj.SetHP(aftHP, True)
            batObj.SetHP(aftHP, isNotify)
    GameWorld.DebugLog("    befHP=%s/%s, aftHP=%s/%s" % (befHP, befMaxHP, aftHP, aftMaxHP))
    GameWorld.DebugLog("    最终属性 ID:%s,atk=%s,def=%s,hp=%s/%s,%s" % (objID, batObj.GetAtk(), batObj.GetDef(), aftHP, aftMaxHP, batObj.GetBatAttrDict()))
    return
def SyncBuffRefresh(turnFight, curBatObj, curBuff, relatedSkillID=0, isNewAdd=False):
    ## @param curBatObj: 该buff的持有者,即在谁身上
    clientPack = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagSCBuffRefresh)
    clientPack.ObjID = curBatObj.GetID()
    clientPack.BuffID = curBuff.GetBuffID()
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnBuffs/BuffAtkType_1001.py
@@ -23,7 +23,7 @@
    skillPer = curSkill.GetSkillPer()
    skillValue = curSkill.GetSkillValue()
    
    hurtValue, hurtTypes = TurnSkill.CalcHurtHP(turnFight, attacker, defender, curSkill, skillValue, skillPer, damageoftime=1)
    hurtValue, hurtTypes = TurnSkill.CalcFormatHurt(turnFight, attacker, defender, curSkill, skillValue, skillPer, damageoftime=1)
    return [hurtValue % ChConfig.Def_PerPointValue, hurtValue / ChConfig.Def_PerPointValue, hurtTypes]
def DoBuffProcess(turnFight, batObj, curBuff, **kwargs):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnBuffs/BuffAtkType_1002.py
@@ -19,7 +19,7 @@
import ChConfig
def CalcBuffValue(turnFight, attacker, defender, curSkill):
    cureHP = TurnSkill.CalcCureHP(turnFight, attacker, defender, curSkill, largeNum=True)
    cureHP = TurnSkill.CalcCureHP(turnFight, attacker, defender, curSkill)
    return [cureHP % ChConfig.Def_PerPointValue, cureHP / ChConfig.Def_PerPointValue]
def DoBuffProcess(turnFight, batObj, curBuff, **kwargs):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnPassive.py
@@ -34,6 +34,8 @@
def OnTriggerPassiveEffect(turnFight, batObj, triggerWay, tagObj=None, connSkill=None, connSkillTypeID=0, connBuff=None, **kwargs):
    ''' 触发被动效果,可能触发技能、buff,需根据优先级触发
    '''
    if not batObj.IsAlive():
        return
    passiveEffMgr = batObj.GetPassiveEffManager()
    effInfoList = passiveEffMgr.GetPassiveEffByTrigger(triggerWay, connSkill, connSkillTypeID, connBuff)
    if not effInfoList:
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnSkill.py
@@ -60,6 +60,9 @@
    if not skillID:
        return
    
    if not curBatObj.IsAlive():
        return
    objID = curBatObj.GetID()
    
    if hasattr(useSkill, "GetRemainTime") and useSkill.GetRemainTime() > 0:
@@ -134,11 +137,7 @@
    if IsNeedSyncSkill(useSkill):
        # 因为可能触发连击,所以标记需带上累计使用技能次数,确保唯一
        useTag = "Skill_%s_%s_%s" % (objID, skillID, curBatObj.GetSkillUseCnt(skillID) + 1)
        clientPack = poolMgr.acquire(ChPyNetSendPack.tagSCTurnFightTag)
        clientPack.Tag = useTag
        clientPack.Len = len(clientPack.Tag)
        clientPack.Sign = 0
        turnFight.addBatPack(clientPack)
        Sync_TurnFightTag(turnFight, useTag, 0)
        
    #这个技能是Buff
    if SkillCommon.IsBuff(useSkill):
@@ -149,11 +148,7 @@
    DoAttackResult(turnFight, curBatObj, useSkill)
    
    if useTag:
        clientPack = poolMgr.acquire(ChPyNetSendPack.tagSCTurnFightTag)
        clientPack.Tag = useTag
        clientPack.Len = len(clientPack.Tag)
        clientPack.Sign = 1
        turnFight.addBatPack(clientPack)
        Sync_TurnFightTag(turnFight, useTag, 1)
        
    # 处理反击 或 连击
    if isTurnNormalSkill:
@@ -596,8 +591,19 @@
def __doAddBuff(turnFight, curBatObj, useSkill):
    #执行添加buff
    
    for tagBatObj in useSkill.GetTagObjList():
        TurnBuff.OnAddBuff(turnFight, tagBatObj, useSkill, buffOwner=curBatObj)
    #光环技能,需先添加施法者
    if useSkill.GetSkillType() == ChConfig.Def_SkillType_Halo:
        curID = curBatObj.GetID()
        skillID = useSkill.GetSkillID()
        GameWorld.DebugLog("光环技能先给施法者添加光源buff! skillID=%s,ownerID=%s" % (skillID, curID))
        if TurnBuff.OnAddBuff(turnFight, curBatObj, useSkill, buffOwner=curBatObj):
            for tagBatObj in useSkill.GetTagObjList():
                if tagBatObj.GetID() == curBatObj.GetID():
                    continue
                TurnBuff.OnAddBuff(turnFight, tagBatObj, useSkill, buffOwner=curBatObj)
    else:
        for tagBatObj in useSkill.GetTagObjList():
            TurnBuff.OnAddBuff(turnFight, tagBatObj, useSkill, buffOwner=curBatObj)
        
    return
@@ -639,6 +645,7 @@
    return
def __doHarmSelf(turnFight, curBatObj, useSkill):
    ## 施法前自残
    harmEff = useSkill.GetEffectByID(ChConfig.SkillEff_UseSkillHarmSelf)
    if not harmEff:
        return
@@ -671,21 +678,52 @@
def SkillModule_1(turnFight, curBatObj, useSkill):
    ## 通用攻击,单攻、群攻
    
    # 计算伤害
    calcHurtResults = []
    for tagBatObj in useSkill.GetTagObjList():
        __doSkillHurtHP(turnFight, curBatObj, tagBatObj, useSkill)
        hurtValue, hurtTypes, immuneHurt = __calcSkillHurt(turnFight, curBatObj, tagBatObj, useSkill)
        calcHurtResults.append([tagBatObj, hurtValue, hurtTypes, immuneHurt])
        
    DoSkillHurtHP(turnFight, curBatObj, useSkill, calcHurtResults)
    return
def __calcSkillHurt(turnFight, atkObj, defObj, curSkill):
    ## 计算技能伤害,只计算值,不做实际处理
    # @return: hurtValue, hurtTypes, immuneHurt
    atkSkillPer = curSkill.GetSkillPer()
    atkSkillValue = curSkill.GetSkillValue()
    hurtValue, hurtTypes = CalcFormatHurt(turnFight, atkObj, defObj, curSkill, atkSkillValue, atkSkillPer)
    hurtValue, hurtTypes, immuneHurt = CalcHurtWithBuff(turnFight, atkObj, defObj, hurtValue, hurtTypes)
    return hurtValue, hurtTypes, immuneHurt
def SkillModule_5(turnFight, curBatObj, useSkill):
    ## 弹射(多次攻击,切换目标,单目标可多次)
    
    ricochetObjList = GetRicochetObjList(turnFight, curBatObj, useSkill)
    ricochetObjList = GetRicochetObjRankList(turnFight, curBatObj, useSkill)
    # 弹射技能需先从弹射目标里预选好曹仁要防护的对象
    caorenProtectObj = None # 曹仁保护的目标,血量最低的,曹仁专用,不挡自己,不挡无敌,顺延直到一个可以挡的最低血量目标,如果没有就不挡
    for tagObj in ricochetObjList:
        tagID = tagObj.GetID()
        if tagID == ChConfig.HeroID_Caoren:
            continue
        if tagObj.CheckInState(ChConfig.BatObjState_Wudi):
            continue
        if not caorenProtectObj or tagObj.GetHP() < caorenProtectObj.GetHP():
            caorenProtectObj = tagObj
    if caorenProtectObj == None:
        caorenProtectObj = 0 # 标记为已筛选过
    # 按顺序一个个弹伤害
    for tagBatObj in ricochetObjList:
        __doSkillHurtHP(turnFight, curBatObj, tagBatObj, useSkill)
        hurtValue, hurtTypes, immuneHurt = __calcSkillHurt(turnFight, curBatObj, tagBatObj, useSkill)
        calcHurtResults = [[tagBatObj, hurtValue, hurtTypes, immuneHurt]]
        DoSkillHurtHP(turnFight, curBatObj, useSkill, calcHurtResults, "RicochetHurt", caorenProtectObj, isManyTimes=True)
        
    return
def GetRicochetObjList(turnFight, curBatObj, useSkill):
def GetRicochetObjRankList(turnFight, curBatObj, useSkill):
    ## 获取弹射目标队列
    ricochetCnt = useSkill.GetTagCount() # 弹射次数,默认取技能目标数
    randEffect = useSkill.GetEffectByID(ChConfig.SkillEff_RandRicochetCnt)
@@ -695,63 +733,120 @@
        ricochetCnt = random.randint(valueA, valueB)
        GameWorld.DebugLog("随机弹射次数: %s,valueA=%s,valueB=%s" % (ricochetCnt, valueA, valueB))
    ricochetCnt += TurnPassive.GetTriggerEffectValue(turnFight, curBatObj, None, ChConfig.PassiveEff_AddRicochetCnt, useSkill)
    GameWorld.DebugLog("弹射次数: %s" % (ricochetCnt))
    
    # 弹射:优先弹射次数少的目标,相同目标可弹射多次
    tagObjList = useSkill.GetTagObjList()
    random.shuffle(tagObjList)
    objCnt = len(tagObjList)
    ricochetObjList = []
    ricochetObjIDList = []
    for index in range(ricochetCnt):
        tagObj = tagObjList[index % objCnt]
        ricochetObjList.append(tagObj)
        ricochetObjIDList.append(tagObj.GetID())
    GameWorld.DebugLog("弹射次数: %s, ricochetObjIDList=%s" % (ricochetCnt, ricochetObjIDList))
    return ricochetObjList
def SkillModule_2(turnFight, curBatObj, useSkill):
    ## 治疗
    
    calcCureResults = []
    relativeObj = GetRelativeObj(turnFight, curBatObj)
    for tagBatObj in useSkill.GetTagObjList():
        __doCureObj(turnFight, curBatObj, tagBatObj, useSkill, relativeObj)
        cureHP = CalcCureHP(turnFight, curBatObj, tagBatObj, useSkill, relativeObj=relativeObj)
        poisonCureOwner = GetPoisonCureOwner(tagBatObj)
        calcCureResults.append([tagBatObj, cureHP, poisonCureOwner])
        
    DoSkillCureHP(turnFight, curBatObj, useSkill, calcCureResults)
    return
def SkillModule_9(turnFight, curBatObj, useSkill):
    ## 弹射治疗(多次治疗,切换目标,单目标可多次)
    
    relativeObj = GetRelativeObj(turnFight, curBatObj)
    ricochetObjList = GetRicochetObjList(turnFight, curBatObj, useSkill)
    ricochetObjList = GetRicochetObjRankList(turnFight, curBatObj, useSkill)
    # 弹射技能需先从弹射目标里预选好曹仁要防护的对象,有毒奶的情况
    poisonCureOwnerDict = {}
    caorenProtectObj = None # 曹仁保护的目标,血量最低的,曹仁专用,不挡自己,不挡无敌,顺延直到一个可以挡的最低血量目标,如果没有就不挡
    for tagObj in ricochetObjList:
        tagID = tagObj.GetID()
        if tagID not in poisonCureOwnerDict:
            poisonCureOwner = GetPoisonCureOwner(tagObj)
            poisonCureOwnerDict[tagID] = poisonCureOwner
        poisonCureOwner = poisonCureOwnerDict[tagID]
        if not poisonCureOwner:
            continue
        if tagID == ChConfig.HeroID_Caoren:
            continue
        if tagObj.CheckInState(ChConfig.BatObjState_Wudi):
            continue
        if not caorenProtectObj or tagObj.GetHP() < caorenProtectObj.GetHP():
            caorenProtectObj = tagObj
    if caorenProtectObj == None:
        caorenProtectObj = 0 # 标记为已筛选过
    for tagBatObj in ricochetObjList:
        __doCureObj(turnFight, curBatObj, tagBatObj, useSkill, relativeObj)
        cureHP = CalcCureHP(turnFight, curBatObj, tagBatObj, useSkill, relativeObj=relativeObj)
        poisonCureOwner = poisonCureOwnerDict.get(tagBatObj.GetID())
        calcCureResults = [[tagBatObj, cureHP, poisonCureOwner]]
        DoSkillCureHP(turnFight, curBatObj, useSkill, calcCureResults, "RicochetCure", caorenProtectObj, isManyTimes=True)
        
    return
def __doCureObj(turnFight, curBatObj, tagBatObj, useSkill, relativeObj=None):
    cureHP = CalcCureHP(turnFight, curBatObj, tagBatObj, useSkill, largeNum=True, relativeObj=relativeObj)
    if cureHP <= 0:
        return
def DoSkillCureHP(turnFight, atkObj, curSkill, calcCureResults, lostType="", caorenProtectObj=None, isManyTimes=False):
    '''结算技能治疗
    @param calcCureResults: 技能公式计算后的伤害结果 [[tagBatObj, cureHP, poisonCureOwner], ...]
    @param caorenProtectObj: 曹仁专用,要防护的对象
    @param isManyTimes: 是否是后端多次计算的
    '''
    
    dID = tagBatObj.GetID()
    hurtObj = useSkill.AddHurtObj(dID)
    if DoPoisonCure(turnFight, tagBatObj, cureHP, hurtObj, useSkill):
        return
    skillID = useSkill.GetSkillID()
    dHP = tagBatObj.GetHP()
    dMapHP = tagBatObj.GetMaxHP()
    remainHP = min(dHP + cureHP, dMapHP)
    realCureHP = max(remainHP - dHP, 0)
    tagBatObj.SetHP(remainHP)
    hurtObj.AddHurtType(ChConfig.HurtTYpe_Recovery)
    hurtObj.SetHurtHP(cureHP)
    hurtObj.SetLostHP(realCureHP)
    hurtObj.SetCurHP(tagBatObj.GetHP())
    GameWorld.DebugLog("    治疗: dID=%s,cureHP=%s,realCureHP=%s,%s/%s" % (dID, cureHP, realCureHP, tagBatObj.GetHP(), dMapHP))
    TurnAttack.AddTurnObjCureHP(tagBatObj, curBatObj, cureHP, realCureHP, skillID)
    skillID = curSkill.GetSkillID()
    calcHurtResults = []
    for tagBatObj, cureHP, poisonCureOwner in calcCureResults:
        # 汇总毒奶
        if poisonCureOwner:
            hurtTypes = pow(2, ChConfig.HurtTYpe_Cure)
            hurtTypes |= pow(2, ChConfig.HurtType_PoisonCureHurt)
            hurtValue = cureHP
            hurtValue, hurtTypes, immuneHurt = CalcHurtWithBuff(turnFight, atkObj, tagBatObj, hurtValue, hurtTypes)
            calcHurtResults.append([tagBatObj, hurtValue, hurtTypes, immuneHurt])
            continue
        # 非毒奶的执行治疗
        dID = tagBatObj.GetID()
        dHP = tagBatObj.GetHP()
        dMapHP = tagBatObj.GetMaxHP()
        remainHP = min(dHP + cureHP, dMapHP)
        realCureHP = max(remainHP - dHP, 0)
        tagBatObj.SetHP(remainHP)
        hurtObj = curSkill.AddHurtObj(dID)
        hurtObj.AddHurtType(ChConfig.HurtTYpe_Cure)
        hurtObj.SetHurtHP(cureHP)
        hurtObj.SetLostHP(realCureHP)
        hurtObj.SetCurHP(tagBatObj.GetHP())
        GameWorld.DebugLog("    治疗: dID=%s,cureHP=%s,realCureHP=%s,%s/%s" % (dID, cureHP, realCureHP, tagBatObj.GetHP(), dMapHP))
        TurnAttack.AddTurnObjCureHP(tagBatObj, atkObj, cureHP, realCureHP, skillID)
    # 再结算毒奶伤害
    DoSkillHurtHP(turnFight, atkObj, curSkill, calcHurtResults, "PoisonCure", caorenProtectObj, isManyTimes)
    return
def GetPoisonCureOwner(defObj):
    ## 获取玩家身上的毒奶buff归属者
    # @return: buffOwner or None-无毒奶buff
    buffMgr = defObj.GetBuffManager()
    poisonCureBuff = buffMgr.FindBuffByState(ChConfig.BatObjState_PoisonCure) # 毒奶暂定唯一
    if not poisonCureBuff:
        return
    ownerID = poisonCureBuff.GetOwnerID()
    buffOwner = BattleObj.GetBatObjMgr().getBatObj(ownerID) # 攻击方
    return buffOwner
def DoCureOfTime(turnFight, batObj, curBuff, cureHP, **kwargs):
    ## 执行持续治疗单次逻辑
@@ -765,19 +860,16 @@
    if not buffOwner:
        return
    
    atkObj = buffOwner
    defObj = batObj
    poisonCureOwner = GetPoisonCureOwner(defObj)
    atkObj = poisonCureOwner if poisonCureOwner else buffOwner
    
    atkID = ownerID
    atkID = atkObj.GetID()
    defID = defObj.GetID()
    tagObjList = [defObj]
    
    poolMgr = ObjPool.GetPoolMgr()
    useSkill = poolMgr.acquire(BattleObj.PySkill, skillIpyData, atkID)
    useSkill.SetTagObjList(tagObjList)
    useSkill.ClearHurtObj()
    hurtObj = useSkill.AddHurtObj(defID)
    hurtObj.AddHurtType(ChConfig.HurtTYpe_Recovery)
    # 结算需要同步标签
    useTag = "Skill_%s_%s_Cot" % (atkID, skillID)
    Sync_TurnFightTag(turnFight, useTag, 0)
    
    dHP = defObj.GetHP()
    dMaxHP = defObj.GetMaxHP()
@@ -792,15 +884,27 @@
    #    hurtValue *= (10000 + FinalDamPer) / 10000.0
    #    GameWorld.DebugLog("    增伤: hurtValue=%s,FinalDamPer=%s" % (hurtValue, FinalDamPer))
    
    if DoPoisonCure(turnFight, defObj, cureHP, hurtObj, useSkill):
        remainHP = defObj.GetHP()
        diffType = 0
        diffValue = hurtObj.GetHurtHP()
    poolMgr = ObjPool.GetPoolMgr()
    useSkill = poolMgr.acquire(BattleObj.PySkill, skillIpyData, atkID)
    useSkill.SetTagObjList([defObj])
    useSkill.SetBatType(ChConfig.TurnBattleType_Cot)
    poisonCureOwner = GetPoisonCureOwner(defObj)
    if poisonCureOwner:
        GameWorld.DebugLog("本次治疗为毒奶: cureHP=%s" % cureHP)
        hurtValue = cureHP
        hurtTypes = pow(2, ChConfig.HurtTYpe_Cure)
        hurtTypes |= pow(2, ChConfig.HurtType_PoisonCureHurt)
        hurtValue, hurtTypes, immuneHurt = CalcHurtWithBuff(turnFight, poisonCureOwner, defObj, hurtValue, hurtTypes)
        calcHurtResults = [[defObj, hurtValue, hurtTypes, immuneHurt]]
        DoSkillHurtHP(turnFight, poisonCureOwner, useSkill, calcHurtResults, "PoisonCOT")
    else:
        remainHP = min(dHP + cureHP, dMaxHP)
        realCureHP = max(remainHP - dHP, 0)
        defObj.SetHP(remainHP)
        
        hurtObj = useSkill.AddHurtObj(defID)
        hurtObj.AddHurtType(ChConfig.HurtTYpe_Cure)
        hurtObj.SetHurtHP(cureHP)
        hurtObj.SetLostHP(realCureHP)
        hurtObj.SetCurHP(defObj.GetHP())
@@ -808,13 +912,12 @@
        
        TurnAttack.AddTurnObjCureHP(defObj, atkObj, cureHP, realCureHP, skillID)
        
        diffType = 1
        diffValue = cureHP
    hurtTypes = hurtObj.GetHurtTypes()
    Sync_PropertyRefreshView(turnFight, defObj, ChConfig.AttrID_HP, remainHP, diffValue, diffType=diffType, skillID=skillID, hurtTypes=hurtTypes)
    Sync_UseSkill(turnFight, atkObj, useSkill)
    
    DoBeAttackResult(turnFight, atkObj, useSkill)
    # 通知结束标签
    Sync_TurnFightTag(turnFight, useTag, 1)
    
    useSkill.ResetUseRec()
    poolMgr.release(useSkill)
@@ -891,7 +994,7 @@
    '''
    
    if curBatObj:
        # 反弹、吸血 的原因,需要统一处理后结算,可能导致HP为负值,修正为0
        # 反弹、吸血、平摊伤害自残 的原因,需要统一处理后结算,可能导致HP为负值,修正为0
        if curBatObj.GetHP() < 0:
            curBatObj.SetHP(0)
            
@@ -996,6 +1099,8 @@
        #GameWorld.DebugLog("同阵营不触发反击!") # 魅惑可能导致打自己人
        return
    
    # 大回合单武将反击次数限制
    canAtkbackDictTypeList = IpyGameDataPY.GetFuncEvalCfg("ParryCfg", 2)
    if atkObj.GetAtkDistType() not in canAtkbackDictTypeList:
        heroID = atkObj.GetHeroID()
@@ -1003,7 +1108,7 @@
        return
    
    canAtkBack = False
    for hurtObj in useSkill.GetHurtObjList():
    for hurtObj in useSkill.GetHurtObjListAll():
        if hurtObj.GetObjID() != tagID:
            continue
        if hurtObj.HaveHurtType(ChConfig.HurtType_Parry): # 格挡时可反击
@@ -1061,16 +1166,22 @@
            
    # 统计击杀
    killObjList = [] # 击杀其他阵营目标列表
    dieObjList = [] # 死亡的单位列表
    for tagObj in useSkill.GetTagObjList():
    dieObjList = [] # 死亡的单位列表,包含友方单位或自己
    tagObjList = useSkill.GetTagObjList() # 主要目标列表
    tagObjListEx = useSkill.GetTagObjListEx() # 额外目标列表
    tagObjListAll = tagObjList + tagObjListEx
    for tagObj in tagObjListAll:
        tagID = tagObj.GetID()
        if tagObj.IsAlive() and tagObj.GetHP() <= 0:
            dieObjList.append(tagObj)
            if tagObj.GetFaction() != curObj.GetFaction():
                killObjList.append(tagObj)
                TurnAttack.SetObjKilled(turnFight, tagObj, curObj, useSkill)
            TurnAttack.SetObjKilled(turnFight, tagObj, curObj, useSkill)
    useSkill.SetKillObjList(killObjList)
    if curObj.IsAlive() and curObj.GetHP() <= 0:
    # 判断自己,因为反弹、平摊伤害的原因,有可能自己干死自己
    selfAlive = curObj.IsAlive()
    if selfAlive and curObj.GetHP() <= 0:
        selfAlive = False
        dieObjList.append(curObj)
        TurnAttack.SetObjKilled(turnFight, curObj)
        
@@ -1079,12 +1190,12 @@
    isSuperHit, isStun, isSuckHP = False, False, False
    missObjIDList, immuneObjIDList = [], [] # 闪避、免疫对象ID列表
    beHurtObjIDList = [] # 受伤的对象ID列表
    for hurtObj in useSkill.GetHurtObjList():
    for hurtObj in useSkill.GetHurtObjListAll():
        hurtObjID = hurtObj.GetObjID()
        tagObj = batObjMgr.getBatObj(hurtObjID)
        if not tagObj:
            continue
        if not hurtObj.HaveHurtType(ChConfig.HurtTYpe_Recovery) and not hurtObj.HaveHurtType(ChConfig.HurtType_Immune) \
        if not hurtObj.HaveHurtType(ChConfig.HurtTYpe_Cure) and not hurtObj.HaveHurtType(ChConfig.HurtType_Immune) \
            and (isTurnNormalSkill or isAngerSkill) and tagObj.IsAlive():
            __doSkillHurtAnger(tagObj, hurtObj.GetLostHP(), useSkill)
            
@@ -1116,7 +1227,7 @@
            isSuckHP = True
            
    # 记录最后一次总伤害,有伤害目标才记录
    if useSkill.GetHurtObjList():
    if totalHurtValue:
        curObj.SetLastHurtValue(totalHurtValue)
        
    # 群攻只触发一次特长
@@ -1135,7 +1246,8 @@
    effIgnoreObjIDList = missObjIDList + immuneObjIDList
    useSkill.SetEffIgnoreObjIDList(effIgnoreObjIDList)
    # 优先触发本技能额外效果,注:仅该技能释放后该技能的额外效果视为主技能的效果,优先级最高
    __DoCurSkillEff(turnFight, curObj, useSkill, effIgnoreObjIDList, isUseSkill)
    if selfAlive:
        __DoCurSkillEff(turnFight, curObj, useSkill, effIgnoreObjIDList, isUseSkill)
    
    # ========== 以下触发被动 ==========
    
@@ -1171,44 +1283,47 @@
    triggerOne = False
    batType = useSkill.GetBatType()
    isAttackDirect = (isUseSkill and SkillCommon.isAttackDirectSkill(useSkill)) # 是否直接攻击
    for tagObj in useSkill.GetTagObjList():
    for tagObj in tagObjListAll:
        tagID = tagObj.GetID()
        if tagID in effIgnoreObjIDList:
            continue
        isExObj = tagObj in tagObjListEx # 是否额外目标
        
        # 掉血时
        if tagID in beHurtObjIDList:
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeHurt, curObj, connSkill=useSkill)
            
        # 直接攻击
        if isAttackDirect:
        if isAttackDirect and not isExObj:
            if not triggerOne:
                TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_AttackOverDirectOne, tagObj, connSkill=useSkill)
            TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_AttackOverDirect, tagObj, connSkill=useSkill)
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeAttackedDirect, curObj, connSkill=useSkill)
        else:
        # 持续伤害
        elif not isAttackDirect:
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeAnyEffect, curObj, connSkill=useSkill)
            # 受到持续伤害
            if tagID in beHurtObjIDList:
            if tagID in beHurtObjIDList and not isExObj:
                TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeDOTHurt, curObj, connSkill=useSkill)
                
        # 使用技能后
        if isUseSkill:
        if isUseSkill and not isExObj:
            if not triggerOne:
                TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_UseSkillOverOne, tagObj, connSkill=useSkill)
            TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_UseSkillOver, tagObj, connSkill=useSkill)
            
        # 连击
        if batType == ChConfig.TurnBattleType_Combo:
        if batType == ChConfig.TurnBattleType_Combo and not isExObj:
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeCombo, curObj, connSkill=useSkill)
        # 追击
        elif batType == ChConfig.TurnBattleType_Pursue:
        elif batType == ChConfig.TurnBattleType_Pursue and not isExObj:
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BePursue, curObj, connSkill=useSkill)
            
        triggerOne = True # 设置已经触发过一次
        
    # 有击杀时验证是否结算,最后处理
    if killObjList:
    # 验证是否结算,最后处理
    if dieObjList:
        turnFight.checkOverByKilled()
        
    return
@@ -1291,6 +1406,8 @@
        return
    specialtyAddXPDict = IpyGameDataPY.GetFuncEvalCfg("AngerXP", 5, {})
    if str(specialty) not in specialtyAddXPDict:
        return
    if not gameObj.IsAlive():
        return
    addXP = specialtyAddXPDict[str(specialty)]
    addXP = GetEnhanceXP(gameObj, addXP)
@@ -1465,55 +1582,8 @@
        
    return isOK
def __doSkillHurtHP(turnFight, attacker, defObj, curSkill):
    ## 执行技能伤血,只计算伤血,其他逻辑等技能同步后再处理
    # @return: None - 没有执行成功,即忽略该目标
    atkObj = attacker
    atkID = atkObj.GetID()
    defID = defObj.GetID()
    skillID = curSkill.GetSkillID()
    hurtObj = curSkill.AddHurtObj(defID)
    atkSkillPer = curSkill.GetSkillPer()
    atkSkillValue = curSkill.GetSkillValue()
    dHP = defObj.GetHP()                # 防守方当前血量
    dMaxHP = defObj.GetMaxHP()          # 防守方最大血量
    zhansha = False # 斩杀特殊处理
    # նɱ
    if zhansha:
        pass
    else:
        hurtValue, hurtTypes = CalcHurtHP(turnFight, atkObj, defObj, curSkill, atkSkillValue, atkSkillPer)
        hurtValue, realHurtHP, hurtTypes = CalcHurtHPWithBuff(turnFight, atkObj, defObj, curSkill, hurtValue, hurtTypes)
        #伤害结构体
        hurtObj.SetHurtTypes(hurtTypes)
        hurtObj.SetHurtHP(hurtValue)
        hurtObj.SetRealHurtHP(realHurtHP)
        remainHP = min(dMaxHP, max(0, dHP - realHurtHP)) # 剩余血量
    remainHP = int(remainHP)    #防范
    lostHP = dHP - remainHP # 实际掉血量
    hurtObj.SetLostHP(lostHP)
    hurtObj.SetCurHP(remainHP)
    defObj.SetHP(remainHP)
    GameWorld.DebugLog("    伤血: atkID=%s,defID=%s,hurtValue=%s,realHurtHP=%s,lostHP=%s,%s/%s"
                       % (atkID, defID, hurtValue, realHurtHP, lostHP, defObj.GetHP(), defObj.GetMaxHP()))
    TurnAttack.AddTurnObjHurtValue(atkObj, defObj, hurtValue, lostHP, skillID)
    #反弹伤害
    CalcBounceHP(turnFight, atkObj, defObj, hurtObj, curSkill)
    #吸血
    CalcSuckBlood(turnFight, atkObj, defObj, hurtObj, curSkill)
    return
def CalcHurtHP(turnFight, atkObj, defObj, curSkill, atkSkillValue, atkSkillPer, **kwargs):
    '''计算伤害,默认按攻击计算
def CalcFormatHurt(turnFight, atkObj, defObj, curSkill, atkSkillValue, atkSkillPer, **kwargs):
    '''按公式计算伤害,默认按攻击计算
    '''
    
    mapID = turnFight.mapID
@@ -1660,7 +1730,6 @@
    GameWorld.DebugLog("伤血计算: atkID=%s,defID=%s,skillID=%s,atkSkillPer=%s,calcType=%s,aAtk=%s,dDef=%s,dHP=%s/%s,hurtTypes=%s,aAddSkillPer=%s,aFinalDamPer=%s" 
                       % (atkID, defID, skillID, atkSkillPer, calcType, aAtk, dDef, dHP, dMaxHP, hurtTypes, aAddSkillPer, aFinalDamPer))
    
    # 持续性伤害
    if isTurnNormalSkill:
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 1))
        GameWorld.DebugLog("    普攻技能伤害=%s,aNormalSkillPer=%s,dNormalSkillPerDef=%s" % (hurtValue, aNormalSkillPer, dNormalSkillPerDef))
@@ -1768,35 +1837,363 @@
        return True
    return False
def CalcHurtHPWithBuff(turnFight, atkObj, defObj, curSkill, hurtValue, hurtTypes=0):
    ## 计算伤害后,因各种buff和状态的影响处理
    # @return: hurtValue, realHurtHP, hurtTypes
def CalcHurtWithBuff(turnFight, atkObj, defObj, hurtValue, hurtTypes=0):
    '''计算buff影响得到最终伤害: 如无敌、增减伤盾等
    @param hurtValue: 公式计算的伤害值
    @return: hurtValue, hurtTypes, immuneHurt
    '''
    
    immuneHurt = 0 # 免疫伤害值
    if hurtValue <= 0:
        return 0, 0, hurtTypes
        hurtValue = 0
        return hurtValue, hurtTypes, immuneHurt
    
    hurtValue = int(hurtValue)
    buffMgr = defObj.GetBuffManager()
    wudiBuffList = buffMgr.FindBuffListByState(ChConfig.BatObjState_Wudi)
    if wudiBuffList:
    if defObj.CheckInState(ChConfig.BatObjState_Wudi):
        hurtTypes |= pow(2, ChConfig.HurtType_Immune) # 添加免疫
        immuneHurt = hurtValue
        hurtValue = 0
        return hurtValue, hurtTypes, immuneHurt
    # 增减伤盾, 会改变 hurtValue,区别与计算公式中的增减伤,这里处理的是对公式计算结果的再次增减
    hurtValue = max(0, hurtValue)
    return hurtValue, hurtTypes, immuneHurt
def DoSkillHurtHP(turnFight, atkObj, curSkill, calcHurtResults, lostType="", caorenProtectObj=None, isManyTimes=False):
    '''结算计算好的技能伤害
    @param calcHurtResults: 技能公式计算后的伤害结果 [[tagBatObj, hurtValue, hurtTypes, immuneHurt], ...]
    @param caorenProtectObj: 曹仁专用,要防护的对象
    @param isManyTimes: 是否是后端多次计算伤害的
    '''
    if not calcHurtResults:
        return
    isAttackDirect = SkillCommon.isAttackDirectSkill(curSkill) # 是否直接攻击
    # 计算平摊
    finalHurtResults = __calcAverageHurt(turnFight, atkObj, curSkill, calcHurtResults, caorenProtectObj, isAttackDirect)
    # 执行最终伤害结算
    mainHurtObj = None
    for hurtInfo in finalHurtResults:
        defObj, hurtValue, hurtTypes, immuneHurt = hurtInfo[:4]
        isEx = hurtInfo[4] if len(hurtInfo) > 4 else 0 # 是否是额外目标
        lostHP = DoLostHP(turnFight, atkObj, defObj, hurtValue, curSkill, lostType, hpCanNegative=True, immuneHurt=immuneHurt)
        #伤害结构体
        defID = defObj.GetID()
        if isEx:
            if isManyTimes and mainHurtObj:
                hurtObj = mainHurtObj.AddHurtObjEx(defID)
            else:
                hurtObj = curSkill.AddHurtObj(defID, isEx=True)
            curSkill.AddTagObjEx(defObj)
        else:
            hurtObj = curSkill.AddHurtObj(defID)
            if not mainHurtObj:
                mainHurtObj = hurtObj
        hurtObj.SetHurtTypes(hurtTypes)
        hurtObj.SetHurtHP(hurtValue)
        hurtObj.SetLostHP(lostHP)
        hurtObj.SetCurHP(max(defObj.GetHP(), 0))
        #反弹伤害:仅非dot的技能造成的伤害技能本身目标才反弹,附属目标不反弹(平摊、溅射  附属目标),反弹伤害不平摊
        if isAttackDirect:
            CalcBounceHP(turnFight, atkObj, defObj, hurtObj, curSkill)
        #吸血:除反弹外的任意伤害都吸
        CalcSuckBlood(turnFight, atkObj, defObj, hurtObj, curSkill)
    return mainHurtObj
def __calcAverageHurt(turnFight, curBatObj, useSkill, calcHurtResults, caorenProtectObj=None, isAttackDirect=False):
    '''计算伤害均摊承伤
    @return: finalHurtResults  [[tagBatObj, hurtValue, hurtTypes, immuneHurt, <isEx>], ...]
    '''
    if not calcHurtResults:
        return calcHurtResults
    # 是否在内层筛选曹仁防护目标,外层如果处理过了但是为None时会设置为0
    checkCaoren = True if (caorenProtectObj == None and isAttackDirect) else False
    if not isAttackDirect or caorenProtectObj == 0:
        caorenProtectObj = None
    srcHurtTotal = 0 # 源总伤害
    hurtValueDict = {} # 最终伤害 {objID:hurtValue, ...}
    immuneHurtDict = {} # 最终免疫伤害 {objID:immuneHurt, ...} # 无敌目标可摊伤害,但免疫该伤害
    tagFaction, tagLineupNum = 0, 0
    for calcInfo in calcHurtResults:
        tagObj, hurtValue, _, immuneHurt = calcInfo
        srcHurtTotal = srcHurtTotal + hurtValue + immuneHurt
        tagID = tagObj.GetID()
        hurtValueDict[tagID] = hurtValue
        if immuneHurt > 0:
            immuneHurtDict[tagID] = immuneHurt
        if not tagFaction:
            tagFaction = tagObj.GetFaction()
            tagLineupNum = tagObj.GetLineupNum()
        # 不挡自己,不挡无敌,顺延直到一个可以挡的最低血量目标,如果没有就不挡
        if checkCaoren and tagID != ChConfig.HeroID_Caoren and not immuneHurt and (not caorenProtectObj or tagObj.GetHP() < caorenProtectObj.GetHP()):
            caorenProtectObj = tagObj
    isAve, caorenProtectID, otherFactionHurtDict = __calcFactionAverageHurt(turnFight, tagFaction, tagLineupNum, hurtValueDict, immuneHurtDict, caorenProtectObj)
    if not isAve:
        #GameWorld.DebugLog("没有分摊伤害原值返回")
        return calcHurtResults
    GameWorld.DebugLog("本阵营分摊伤害结果: %s,免疫伤害=%s,给敌阵营=%s,caorenProtectID=%s"
                       % (hurtValueDict, immuneHurtDict, otherFactionHurtDict, caorenProtectID))
    # 均摊给其他阵营的算非直接伤害,即曹仁无效
    for key, otherHurtDict in otherFactionHurtDict.items():
        aveFaction, aveLineupNum = key
        isAveEx, _, otherFactionHurtDictEx = __calcFactionAverageHurt(turnFight, aveFaction, aveLineupNum, otherHurtDict, immuneHurtDict)
        # 其他阵营分摊后的最终伤害累加到实际最终伤害
        for objID, aveHurt in otherHurtDict.items():
            hurtValueDict[objID] = hurtValueDict.get(objID, 0) + aveHurt
        GameWorld.DebugLog("其他阵营分摊后的最终伤害: isAveEx=%s,%s,免疫伤害=%s,给敌阵营=%s"
                           % (isAveEx, hurtValueDict, immuneHurtDict, otherFactionHurtDictEx))
        # 其他阵营再次分摊给其他阵营的,董白效果引起
        # 董白暂时只能再次摊给源头一次伤害,在多V多场景下暂不做支持 A董白 摊 B董白 摊 C董白,目前只做 A->B->A
        for keyEx, otherHurtDictEx in otherFactionHurtDictEx.items():
            factionEx, lineupNumEx = keyEx
            if factionEx != tagFaction or lineupNumEx != tagLineupNum:
                continue
            for objID, aveHurt in otherHurtDictEx.items():
                hurtValueDict[objID] = hurtValueDict.get(objID, 0) + aveHurt
            GameWorld.DebugLog("其他阵营再分摊给源阵营的伤害: %s,最终伤害=%s,免疫伤害=%s"
                               % (otherHurtDictEx, hurtValueDict, immuneHurtDict))
    finalHurtTotal = 0
    finalHurtResults = []
    # 主要目标伤害更新
    for calcInfo in calcHurtResults:
        tagObj, _, hurtTypes, _ = calcInfo
        tagID = tagObj.GetID()
        finalHurt = int(hurtValueDict.pop(tagID, 0))
        immuneHurt = max(int(immuneHurtDict.pop(tagID, 0)), 0) # 因为有标记-1值,所以加max
        # 主要目标的免疫标记外层已经处理,仅额外处理标记曹仁防护
        if caorenProtectID == tagID:
            hurtTypes |= pow(2, ChConfig.HurtType_CaorenProtect) # 标记受到曹仁防护,前端表现需要
        finalHurtResults.append([tagObj, finalHurt, hurtTypes, immuneHurt])
        finalHurtTotal = finalHurtTotal + finalHurt + immuneHurt
        GameWorld.DebugLog("得出主要目标分摊后的最终伤害: tagID=%s,finalHurt=%s,hurtTypes=%s,immuneHurt=%s,finalHurtTotal=%s" % (tagID, finalHurt, hurtTypes, immuneHurt, finalHurtTotal))
    # 剩下伤害则默认为额外目标伤害,放主要目标后面
    isEx = 1
    batObjMgr = BattleObj.GetBatObjMgr()
    for tagID in set(hurtValueDict.keys() + immuneHurtDict.keys()):
        tagObj = batObjMgr.getBatObj(tagID)
        if not tagObj:
            continue
        hurtTypes = pow(2, ChConfig.HurtType_Normal)
        finalHurt = int(hurtValueDict.pop(tagID, 0))
        immuneHurt = max(int(immuneHurtDict.pop(tagID, 0)), 0)
        if immuneHurt > 0:
            hurtTypes |= pow(2, ChConfig.HurtType_Immune) # 添加免疫
        finalHurtTotal = finalHurtTotal + finalHurt + immuneHurt
        GameWorld.DebugLog("得出额外目标分摊后的最终伤害: tagID=%s,finalHurt=%s,hurtTypes=%s,immuneHurt=%s,finalHurtTotal=%s" % (tagID, finalHurt, hurtTypes, immuneHurt, finalHurtTotal))
        if finalHurt > 0 or immuneHurt > 0: # 额外目标有受伤或免疫才算
            finalHurtResults.append([tagObj, finalHurt, hurtTypes, immuneHurt, isEx])
    if finalHurtTotal != srcHurtTotal:
        GameWorld.DebugLog("[分摊伤害后误差值]=%s,srcHurtTotal=%s,finalHurtTotal=%s" % (srcHurtTotal - finalHurtTotal, srcHurtTotal, finalHurtTotal))
    return finalHurtResults
def __calcFactionAverageHurt(turnFight, faction, lineupNum, hurtValueDict, immuneHurtDict, caorenProtectObj=None):
    '''计算某个阵营的伤害平摊,每个阵营独立计算即可,减少复杂度
        优先级: 甄宓 > 曹仁 > 董白;
        减少复杂度,固定把董白最后算,这样每个阵营的伤害单独计算均摊即可
        @param hurtValueDict: 目标伤害字典
        @param immuneHurtDict: 目前最终免疫伤害字典
        @param caorenProtectObj: 曹仁防护对象,可能为 None 或 0 或 某一个对象
    '''
    # 甄宓    潜能1    织命    战斗开始,甄宓链接全体队友,使任意队友受到的伤害由全体均摊,持续2回合
    # 曹仁的分担触发条件暂时程序内固定,有修改时程序同步修改
    # 曹仁: 我方英雄受到直接攻击时(受到群攻时选择血量最低的友方进行防护),曹仁有30%概率为其分担一半所受伤害,每成功分担一次,曹仁获得1层坚韧,使我方全体减伤提升5%,最多6层,曹仁死亡效果消失
    # 董白怒气    同命锁    对敌方攻击最高的1个目标造成攻击力450%物理伤害,并与其连接,使两人受到的任意伤害平摊(平摊伤害不会被再次平摊),持续2回合
    caorenSkillID = 1015070
    effHeroIDList = [ChConfig.HeroID_Zhenfu, ChConfig.HeroID_Caoren, ChConfig.HeroID_Dongbai] # 暂固定,有增加武将再同步修改
    isAve = False
    caorenProtectID = 0 # 曹仁成功防护的对象ID,有值代表有防护
    otherFactionHurtDict = {} # 分摊给其他阵营的伤害 {(faction, lineupNum):{objID:aveHurt, ...}, ...}
    batFaction = turnFight.getBatFaction(faction)
    batLineup = batFaction.getBatlineup(lineupNum)
    batObjMgr = BattleObj.GetBatObjMgr()
    GameWorld.DebugLog("----开始计算阵营分摊伤害: faction=%s,lineupNum=%s,hurtValueDict=%s,免疫伤害=%s"
                       % (faction, lineupNum, hurtValueDict, immuneHurtDict))
    # 按优先级顺序处理拥有分摊效果的武将
    for effHeroID in effHeroIDList:
        if effHeroID not in batLineup.heroObjIDDict:
            continue
        objID = batLineup.heroObjIDDict[effHeroID]
        batObj = batObjMgr.getBatObj(objID)
        if not batObj or not batObj.IsAlive():
            continue
        buffMgr = batObj.GetBuffManager()
        inHurt = objID in hurtValueDict # 光环里的人员是否有受伤
        aveBuff = None
        aveObjList = [] # 可均摊伤害的对象列表
        if effHeroID == ChConfig.HeroID_Caoren:
            if not caorenProtectObj:
                GameWorld.DebugLog("没有曹仁可防护的对象")
                continue
            hpLowestID = caorenProtectObj.GetID()
            if hpLowestID == objID:
                GameWorld.DebugLog("曹仁不防护自己")
                continue
            if not inHurt and hpLowestID not in hurtValueDict:
                GameWorld.DebugLog("曹仁及防护对象都不在受伤列表里不处理! hpLowestID=%s" % hpLowestID)
                continue
            if batObj.IsInControlledHard():
                GameWorld.DebugLog("曹仁被硬控中不防护")
                continue
            caoRenSkill = batObj.GetSkillManager().FindSkillByID(caorenSkillID)
            if not caoRenSkill:
                GameWorld.DebugLog("曹仁还未学习分担伤害技能! caorenSkillID=%s" % caorenSkillID)
                continue
            if not GameWorld.CanHappen(caoRenSkill.GetHappenRate()):
                GameWorld.DebugLog("曹仁概率未触发防护")
                continue
            inHurt = True
            aveObjList = [batObj, caorenProtectObj]
            GameWorld.DebugLog("曹仁触发防护! hpLowestID=%s" % hpLowestID)
        elif effHeroID == ChConfig.HeroID_Zhenfu:
            aveBuff = buffMgr.FindBuffByState(ChConfig.BatObjState_Zhiming)
        elif effHeroID == ChConfig.HeroID_Dongbai:
            aveBuff = buffMgr.FindBuffByState(ChConfig.BatObjState_Link)
        else:
            continue
        if not aveObjList: # 没有指定均摊伤害的,根据均摊光环buff找
            if not aveBuff:
                GameWorld.DebugLog("没有分摊伤害光环! effHeroID=%s" % (effHeroID))
                continue
            haloObjIDList = aveBuff.GetHaloObjIDList()
            GameWorld.DebugLog("[分摊光环buff] effHeroID=%s,skillID=%s,objID=%s,haloObjIDList=%s" % (effHeroID, aveBuff.GetSkillID(), objID, haloObjIDList))
            for haloObjID in haloObjIDList:
                haloObj = batObjMgr.getBatObj(haloObjID)
                if not haloObj or not haloObj.IsAlive():
                    continue
                aveObjList.append(haloObj)
                if not inHurt and haloObjID in hurtValueDict:
                    inHurt = True
        if not inHurt:
            GameWorld.DebugLog("相关人员没有受伤,不处理分摊伤害! effHeroID=%s" % effHeroID)
            continue
        aveObjCnt = len(aveObjList)
        if aveObjCnt < 2:
            GameWorld.DebugLog("相关人员少于2个时自行承担所有伤害! effHeroID=%s,aveObjCnt=%s" % (effHeroID, aveObjCnt))
            continue
        if not isAve:
            isAve = True
        if effHeroID == ChConfig.HeroID_Caoren and caorenProtectObj:
            caorenProtectID = caorenProtectObj.GetID()
            GameWorld.DebugLog("[曹仁防护目标] caorenProtectID=%s,caorenObjID=%s" % (caorenProtectID, objID))
        aveHurtTotal = 0
        for aveObj in aveObjList:
            aveObjID = aveObj.GetID()
            if effHeroID == ChConfig.HeroID_Caoren and aveObjID == objID:
                GameWorld.DebugLog("    曹仁的效果不平摊曹仁自身的伤害,只是曹仁帮别人摊! aveObjID=%s" % aveObjID)
                continue
            if aveObjID not in hurtValueDict:
                continue
            hurtValue = hurtValueDict.pop(aveObjID, 0) # 被分摊的伤害要移除
            if hurtValue <= 0:
                GameWorld.DebugLog("    无伤害的不处理:可能是非主要目标或自身已经无敌免疫了伤害! aveObjID=%s" % aveObjID)
                continue
            aveHurtTotal += hurtValue
            GameWorld.DebugLog("    累计待分摊伤害! effHeroID=%s,aveObjID=%s,hurtValue=%s,aveHurtTotal=%s" % (effHeroID, aveObjID, hurtValue, aveHurtTotal))
        # 大家分摊
        aveHurtDict = {} # 本阵营该分摊效果分摊伤害记录
        aveHurt = round(aveHurtTotal / float(aveObjCnt), 2)
        GameWorld.DebugLog("计算分摊伤害! effHeroID=%s,aveHurtTotal=%s,aveObjCnt=%s,aveHurt=%s" % (effHeroID, aveHurtTotal, aveObjCnt, aveHurt))
        for aveObj in aveObjList:
            aveObjID = aveObj.GetID()
            aveFaction = aveObj.GetFaction()
            immuneHurt = immuneHurtDict.get(aveObjID)
            if immuneHurt == None:
                if aveObj.CheckInState(ChConfig.BatObjState_Wudi):
                    immuneHurt = 0
                else:
                    immuneHurt = -1 # 标记没有无敌
                immuneHurtDict[aveObjID] = immuneHurt
            if immuneHurt >= 0:
                immuneHurtDict[aveObjID] = immuneHurt + aveHurt
                GameWorld.DebugLog("    累加免疫分摊伤害! aveObjID=%s,免疫伤害=%s" % (aveObjID, immuneHurtDict))
                continue
            if aveFaction == faction:
                aveHurtDict[aveObjID] = aveHurtDict.get(aveObjID, 0) + aveHurt
                GameWorld.DebugLog("    累加本效果本阵营分摊伤害! aveObjID=%s,aveHurtDict=%s" % (aveObjID, aveHurtDict))
            else:
                aveLineupNum = aveObj.GetLineupNum()
                key = (aveFaction, aveLineupNum)
                if key not in otherFactionHurtDict:
                    otherFactionHurtDict[key] = {}
                otherHurtDict = otherFactionHurtDict[key]
                otherHurtDict[aveObjID] = otherHurtDict.get(aveObjID, 0) + aveHurt
                GameWorld.DebugLog("    累加本效果敌阵营分摊伤害! aveObjID=%s,factionKey=%s,otherHurtDict=%s" % (aveObjID, key, otherHurtDict))
        # 汇总最新伤害
        for aveObjID, aveHurt in aveHurtDict.items():
            hurtValueDict[aveObjID] = hurtValueDict.get(aveObjID, 0) + aveHurt
        GameWorld.DebugLog("    更新本效果分摊后的伤害! %s,免疫伤害=%s,给敌阵营=%s" % (hurtValueDict, immuneHurtDict, otherFactionHurtDict))
    return isAve, caorenProtectID, otherFactionHurtDict
def DoLostHP(turnFight, atkObj, defObj, hurtValue, curSkill, lostType="", hpCanNegative=False, immuneHurt=0):
    '''仅执行掉血逻辑: 承伤盾抵扣  + 剩余扣血
    @param hurtValue: 掉血伤害值,一般是各种计算后的最终伤害值
    @param hpCanNegative: 扣除后的生命是否允许负值
    @param immuneHurt: 免疫的伤害值
    @param isSkillSelfTag: 是否技能自身的直接目标,如平摊伤害目标、溅射伤害目标这种就不算直接目标
    @return: lostHP
    '''
    if immuneHurt > 0:
        buffMgr = defObj.GetBuffManager()
        wudiBuffList = buffMgr.FindBuffListByState(ChConfig.BatObjState_Wudi)
        for buff in wudiBuffList:
            skillData = buff.GetSkillData()
            buffSkillID = buff.GetSkillID()
            # 记录免疫积攒的伤害
            buffValue = buff.GetValue1() + buff.GetValue2() * ChConfig.Def_PerPointValue
            updBuffValue = buffValue + hurtValue
            updBuffValue = buffValue + immuneHurt
            buff.SetValue1(updBuffValue % ChConfig.Def_PerPointValue)
            buff.SetValue2(updBuffValue / ChConfig.Def_PerPointValue)
            GameWorld.DebugLog("    无敌盾免疫伤害: defID=%s,buffID=%s,skillID=%s,updBuffValue=%s"
                               % (defObj.GetID(), buff.GetBuffID(), skillData.GetSkillID(), updBuffValue))
        return 0, 0, hurtTypes
            GameWorld.DebugLog("    无敌盾累计免疫伤害: defID=%s,buffID=%s,buffSkillID=%s,buffValue=%s,immuneHurt=%s,updBuffValue=%s,lostType=%s"
                               % (defObj.GetID(), buff.GetBuffID(), buffSkillID, buffValue, immuneHurt, updBuffValue, lostType))
    if hurtValue <= 0:
        return 0
    
    # 减伤盾减伤, 会改变 hurtValue
    hurtValue = max(0, hurtValue)
    atkID = atkObj.GetID()
    defID = defObj.GetID()
    skillID = curSkill.GetSkillID()
    
    # 承伤盾承伤,剩余时间短的优先承伤,承伤不影响输出,相当于额外的HP,仅用于抵扣掉血,仅 改变 realHurtHP
    realHurtHP = hurtValue
    # 承伤盾承伤,剩余时间短的优先承伤,承伤不影响输出,相当于额外的HP,仅用于抵扣掉血,仅 改变 lostHP
    lostHP = hurtValue
    shieldBuffList = []
    buffMgr = defObj.GetBuffManager()
    for buff in buffMgr.FindBuffListByState(ChConfig.BatObjState_DamShield):
        skillData = buff.GetSkillData()
        remainTime = buff.GetRemainTime() # 剩余回合
@@ -1834,9 +2231,20 @@
                buff.SetValue2(updShieldValue / ChConfig.Def_PerPointValue)
                curSkill.AddAfterLogic(ChConfig.AfterLogic_SyncBuff, [defObj, buff, atkObj, "ReduceShieldValue"])
                
        realHurtHP = int(shieldHurtValue / shieldHurtPer)
        GameWorld.DebugLog("        承伤后剩余伤血: realHurtHP=%s" % (realHurtHP))
    return hurtValue, max(0, realHurtHP), hurtTypes
        lostHP = int(shieldHurtValue / shieldHurtPer)
        GameWorld.DebugLog("        承伤后剩余伤血: lostHP=%s" % (lostHP))
    dHP = defObj.GetHP()
    remainHP = dHP - lostHP # 剩余血量
    if not hpCanNegative and remainHP < 0:
        remainHP = 0
    lostHP = dHP - remainHP # 实际掉血量
    defObj.SetHP(remainHP)
    GameWorld.DebugLog("    扣血: atkID=%s,defID=%s,hurtValue=%s,lostType=%s,lostHP=%s,dHP=%s,updHP=%s/%s"
                       % (atkID, defID, hurtValue, lostType, lostHP, dHP, defObj.GetHP(), defObj.GetMaxHP()))
    TurnAttack.AddTurnObjHurtValue(atkObj, defObj, hurtValue, lostHP, skillID, lostType)
    return lostHP
def CalcBounceHP(turnFight, atkObj, defObj, hurtObj, curSkill):
    '''计算反弹反弹伤害
@@ -1858,18 +2266,12 @@
        return
    
    GameWorld.DebugLog("    反弹伤害=%s,%s/%s, damBackPer=%s" % (bounceHP, atkObj.GetHP(), atkObj.GetMaxHP(), damBackPer))
    bounceHP, realBounceHP, _ = CalcHurtHPWithBuff(turnFight, defObj, atkObj, curSkill, bounceHP)
    bounceHP, _, immuneHurt = CalcHurtWithBuff(turnFight, atkObj, defObj, bounceHP)
    if bounceHP <= 0:
        GameWorld.DebugLog("        bounceHP=%s" % (bounceHP))
        return
    hurtObj.SetBounceHP(bounceHP)
    if realBounceHP > 0:
        remainHP = atkObj.GetHP() - realBounceHP # 反弹允许弹到负值,如果最终吸血没有吸到正值则算死亡
        atkObj.SetHP(remainHP)
        GameWorld.DebugLog("        bounceHP=%s,realBounceHP=%s,%s/%s" % (bounceHP, realBounceHP, atkObj.GetHP(), atkObj.GetMaxHP()))
    TurnAttack.AddTurnObjHurtValue(defObj, atkObj, bounceHP, realBounceHP, isBounce=True)
    DoLostHP(turnFight, atkObj, defObj, bounceHP, curSkill, "Bounce", hpCanNegative=True, immuneHurt=immuneHurt) # 反弹后生命允许负值
    return
def CalcSuckBlood(turnFight, atkObj, defObj, hurtObj, curSkill):
@@ -1886,7 +2288,17 @@
    if suckHP <= 0:
        return
    
    if DoPoisonCure(turnFight, atkObj, suckHP, hurtObj, curSkill, True):
    # 吸血毒奶
    poisonCureOwner = GetPoisonCureOwner(atkObj)
    if poisonCureOwner:
        atkObj, defObj = poisonCureOwner, atkObj
        hurtValue = suckHP
        hurtValue, _, immuneHurt = CalcHurtWithBuff(turnFight, atkObj, defObj, hurtValue)
        GameWorld.DebugLog("    本次吸血为毒奶: suckHP=%s,hurtValue=%s" % (suckHP, hurtValue))
        DoLostHP(turnFight, atkObj, defObj, hurtValue, curSkill, "PoisonSuckBlood", hpCanNegative=True, immuneHurt=immuneHurt) # 吸血毒奶生命允许负值
        hurtObj.AddHurtType(ChConfig.HurtType_PoisonCureSuck)
        hurtObj.SetSuckHP(hurtValue) # 如果前端有需要表现免疫吸血毒奶,可以判断吸血毒奶下该值为0代表免疫
        return
    
    curHP = atkObj.GetHP()
@@ -1902,40 +2314,7 @@
    TurnAttack.AddTurnObjCureHP(atkObj, atkObj, suckHP, cureHP)
    return
def DoPoisonCure(turnFight, curObj, cureHP, hurtObj, curSkill, isSuck=False):
    ## 毒奶逻辑
    buffMgr = curObj.GetBuffManager()
    poisonCureBuff = buffMgr.FindBuffByState(ChConfig.BatObjState_PoisonCure) # 毒奶暂定唯一
    if not poisonCureBuff:
        return
    ownerID = poisonCureBuff.GetOwnerID()
    buffOwner = BattleObj.GetBatObjMgr().getBatObj(ownerID) # 攻击方
    if not buffOwner:
        return
    atkObj, defObj = buffOwner, curObj
    hurtTypes = hurtObj.GetHurtTypes()
    hurtValue, realHurtHP, hurtTypes = CalcHurtHPWithBuff(turnFight, atkObj, defObj, curSkill, cureHP, hurtTypes)
    dHP = defObj.GetHP()
    remainHP = max(0, dHP - realHurtHP) # 剩余血量
    lostHP = dHP - remainHP # 实际掉血量
    defObj.SetHP(remainHP)
    GameWorld.DebugLog("    毒奶: hurtValue=%s,realHurtHP=%s,lostHP=%s,%s/%s,isSuck=%s" % (hurtValue, realHurtHP, lostHP, defObj.GetHP(), defObj.GetMaxHP(), isSuck))
    if isSuck:
        hurtTypes |= pow(2, ChConfig.HurtType_PoisonCureSuck)
        hurtObj.SetSuckHP(hurtValue)
    else:
        hurtTypes |= pow(2, ChConfig.HurtType_PoisonCureHurt)
        hurtObj.SetHurtHP(hurtValue)
        hurtObj.SetLostHP(lostHP)
    hurtObj.SetHurtTypes(hurtTypes)
    TurnAttack.AddTurnObjHurtValue(atkObj, defObj, hurtValue, lostHP, curSkill.GetSkillID())
    return True
def CalcCureHP(turnFight, userObj, tagObj, curSkill, largeNum=False, relativeObj=None):
def CalcCureHP(turnFight, userObj, tagObj, curSkill, relativeObj=None):
    ''' 计算治疗值
    '''
    
@@ -1943,34 +2322,13 @@
    if not relativeObj:
        relativeObj = GetRelativeObj(turnFight, userObj)
        
    skillID = curSkill.GetSkillID()
    cureType = curSkill.GetCalcType()
    skillPer = curSkill.GetSkillPer()
    #skillValue = curSkill.GetSkillValue()
    
    skillPer += GetAddSkillPer(turnFight, userObj, tagObj, curSkill)
    
    cureHP = __calcCureHP(turnFight, userObj, tagObj, relativeObj, curSkill, cureType, skillPer, largeNum)
    cureWayExEff = curSkill.GetEffectByID(ChConfig.SkillEff_CureWayEx)
    if cureWayExEff:
        cureType = cureWayExEff.GetEffectValue(0)
        skillPer = cureWayExEff.GetEffectValue(1)
        GameWorld.DebugLog("额外计算治疗方式: cureType=%s,skillPer=%s" % (cureType, skillPer))
        cureHPEx = __calcCureHP(turnFight, userObj, tagObj, relativeObj, curSkill, cureType, skillPer, largeNum)
        cureHP += cureHPEx
    hurtShareEff = curSkill.GetEffectByID(ChConfig.SkillEff_HurtShare)
    if hurtShareEff:
        tagCnt = max(1, len(curSkill.GetTagObjList()))
        cureHP = cureHP / tagCnt
        GameWorld.DebugLog("    目标均摊治疗: cureHP=%s,tagCnt=%s" % (cureHP, tagCnt))
    return max(1, int(cureHP)) # 保底1点
def __calcCureHP(turnFight, userObj, tagObj, relativeObj, curSkill, cureType, skillPer, largeNum=False):
    cureBaseValue = GetCalcBaseValue(cureType, userObj, tagObj, curSkill)
    # 回合制
    curePer = userObj.GetBatAttrValue(ChConfig.AttrID_CurePer) # 治疗加成
    cureDefPer = relativeObj.GetBatAttrValue(ChConfig.AttrID_CurePerDef) # 对位的弱化治疗
    angerOverflow = 0 # 怒气溢出值
@@ -1981,16 +2339,31 @@
    curePer /= float(ChConfig.Def_MaxRateValue)
    cureDefPer /= float(ChConfig.Def_MaxRateValue)
    
    baseValue = max(0, cureBaseValue) # 防止基值被弱化为负值,在恢复比例也是负值的情况下负负得正导致可以恢复血量
    baseValue = GetCalcBaseValue(cureType, userObj, tagObj, curSkill)
    
    #公式计算治疗值
    cureHP = eval(IpyGameDataPY.GetFuncCompileCfg("CureFormula", 1))
    if not largeNum:
        cureHP = min(cureHP, ChConfig.Def_UpperLimit_DWord)
    cureHP = max(1, int(cureHP)) # 保底1点
    GameWorld.DebugLog("计算治疗值(%s):skillID=%s,cureType=%s,baseValue=%s,skillPer=%s,curePer=%s,cureDefPer=%s,angerOverflow=%s" 
                       % (cureHP, curSkill.GetSkillID(), cureType, baseValue, skillPer, curePer, cureDefPer, angerOverflow))
                       % (cureHP, skillID, cureType, baseValue, skillPer, curePer, cureDefPer, angerOverflow))
    # 额外治疗值
    cureWayExEff = curSkill.GetEffectByID(ChConfig.SkillEff_CureWayEx)
    if cureWayExEff:
        cureType = cureWayExEff.GetEffectValue(0)
        skillPer = cureWayExEff.GetEffectValue(1)
        baseValue = GetCalcBaseValue(cureType, userObj, tagObj, curSkill)
        cureHPEx = eval(IpyGameDataPY.GetFuncCompileCfg("CureFormula", 1))
        cureHP += cureHPEx
        GameWorld.DebugLog("    额外治疗值(%s): cureType=%s,skillPer=%s,cureHP=%s" % (cureHPEx, cureType, skillPer, cureHP))
    hurtShareEff = curSkill.GetEffectByID(ChConfig.SkillEff_HurtShare)
    if hurtShareEff:
        tagCnt = max(1, len(curSkill.GetTagObjList()))
        cureHP = cureHP / tagCnt
        GameWorld.DebugLog("    目标均摊治疗: cureHP=%s,tagCnt=%s" % (cureHP, tagCnt))
    if cureHP < 1:
        cureHP = 1
        GameWorld.DebugLog("    保底治疗值: cureHP=%s" % (cureHP))
    return cureHP
def GetCalcBaseValue(calcType, curObj, tagObj, curSkill):
@@ -2020,12 +2393,12 @@
        if bySkill:
            bySkillID = bySkill.GetSkillID()
            curID = curObj.GetID()
            for hurtObj in bySkill.GetHurtObjList():
            for hurtObj in bySkill.GetHurtObjListAll():
                if curID == hurtObj.GetObjID():
                    baseValue += hurtObj.GetHurtHP()
        GameWorld.DebugLog("根据最后受击技能伤害: %s,bySkillID=%s" % (baseValue, bySkillID))
        
    return baseValue
    return max(0, baseValue)
def DoDOTAttack(turnFight, batObj, curBuff, hurtValue, hurtTypes, **kwargs):
    ## 执行单次dot逻辑
@@ -2044,17 +2417,15 @@
    
    atkID = ownerID
    defID = defObj.GetID()
    tagObjList = [defObj]
    
    poolMgr = ObjPool.GetPoolMgr()
    useSkill = poolMgr.acquire(BattleObj.PySkill, skillIpyData, atkID)
    useSkill.SetTagObjList(tagObjList)
    useSkill.ClearHurtObj()
    hurtObj = useSkill.AddHurtObj(defID)
    # 结算需要同步标签
    useTag = "Skill_%s_%s_Dot" % (atkID, skillID)
    Sync_TurnFightTag(turnFight, useTag, 0)
    
    dHP = defObj.GetHP()
    GameWorld.DebugLog("结算dot: atkID=%s,defID=%s,buffID=%s,skillID=%s,ownerID=%s,hurtValue=%s,hurtTypes=%s,dHP=%s"
                       % (atkID, defID, buffID, skillID, ownerID, hurtValue, hurtTypes, dHP))
    dMaxHP = defObj.GetMaxHP()
    GameWorld.DebugLog("结算dot: atkID=%s,defID=%s,buffID=%s,skillID=%s,ownerID=%s,hurtValue=%s,hurtTypes=%s,dHP=%s/%s"
                       % (atkID, defID, buffID, skillID, ownerID, hurtValue, hurtTypes, dHP, dMaxHP))
    layer = curBuff.GetLayer()
    if layer > 0:
        hurtValue *= layer
@@ -2063,27 +2434,34 @@
        FinalDamPer = kwargs["FinalDamPer"]
        hurtValue *= (10000 + FinalDamPer) / 10000.0
        GameWorld.DebugLog("    增伤: hurtValue=%s,FinalDamPer=%s" % (hurtValue, FinalDamPer))
    hurtValue, realHurtHP, hurtTypes = CalcHurtHPWithBuff(turnFight, atkObj, defObj, useSkill, hurtValue, hurtTypes)
    poolMgr = ObjPool.GetPoolMgr()
    useSkill = poolMgr.acquire(BattleObj.PySkill, skillIpyData, atkID)
    useSkill.SetTagObjList([defObj])
    useSkill.SetBatType(ChConfig.TurnBattleType_Dot)
    
    remainHP = max(0, dHP - realHurtHP) # 剩余血量
    lostHP = dHP - remainHP # 实际掉血量
    defObj.SetHP(remainHP)
    GameWorld.DebugLog("    hurtValue=%s,realHurtHP=%s,lostHP=%s,%s/%s" % (hurtValue, realHurtHP, lostHP, defObj.GetHP(), defObj.GetMaxHP()))
    hurtValue, hurtTypes, immuneHurt = CalcHurtWithBuff(turnFight, atkObj, defObj, hurtValue, hurtTypes)
    calcHurtResults = [[defObj, hurtValue, hurtTypes, immuneHurt]]
    DoSkillHurtHP(turnFight, atkObj, useSkill, calcHurtResults, "DOT")
    
    hurtObj.SetHurtTypes(hurtTypes)
    hurtObj.SetHurtHP(hurtValue)
    hurtObj.SetRealHurtHP(realHurtHP)
    hurtObj.SetLostHP(lostHP)
    hurtObj.SetCurHP(remainHP)
    TurnAttack.AddTurnObjHurtValue(atkObj, batObj, hurtValue, lostHP, skillID)
    Sync_PropertyRefreshView(turnFight, defObj, ChConfig.AttrID_HP, remainHP, hurtValue, diffType=0, skillID=skillID, hurtTypes=hurtTypes)
    Sync_UseSkill(turnFight, atkObj, useSkill)
    
    DoBeAttackResult(turnFight, atkObj, useSkill)
    
    # 通知结束标签
    Sync_TurnFightTag(turnFight, useTag, 1)
    useSkill.ResetUseRec()
    poolMgr.release(useSkill)
    return
def Sync_TurnFightTag(turnFight, useTag, sign):
    ## 通知技能标签
    clientPack = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagSCTurnFightTag)
    clientPack.Tag = useTag
    clientPack.Len = len(clientPack.Tag)
    clientPack.Sign = sign
    turnFight.addBatPack(clientPack)
    return
def Sync_UseSkill(turnFight, curBatObj, useSkill):
@@ -2099,6 +2477,7 @@
    clientPack.CurHPEx = curBatObj.GetHP() / ChConfig.Def_PerPointValue
    clientPack.SkillID = useSkill.GetSkillID()
    clientPack.RelatedSkillID = relatedSkillID
    # 技能的主要伤害
    clientPack.HurtList = []
    for hurtObj in useSkill.GetHurtObjList():
        hurt = poolMgr.acquire(ChPyNetSendPack.tagSCUseSkillHurt)
@@ -2110,7 +2489,36 @@
        hurt.CurHPEx = hurtObj.GetCurHP() / ChConfig.Def_PerPointValue
        hurt.SuckHP = min(hurtObj.GetSuckHP(), ChConfig.Def_UpperLimit_DWord)
        hurt.BounceHP = min(hurtObj.GetBounceHP(), ChConfig.Def_UpperLimit_DWord)
        hurt.HurtListEx = []
        # 单个伤害的额外伤害,如弹射+平摊伤害
        for hurtObjEx in hurtObj.GetHurtObjListEx():
            hurtEx = poolMgr.acquire(ChPyNetSendPack.tagSCUseSkillHurtEx)
            hurtEx.ObjID = hurtObjEx.GetObjID()
            hurtEx.AttackTypes = hurtObjEx.GetHurtTypes()
            hurtEx.HurtHP = hurtObjEx.GetHurtHP() % ChConfig.Def_PerPointValue
            hurtEx.HurtHPEx = hurtObjEx.GetHurtHP() / ChConfig.Def_PerPointValue
            hurtEx.CurHP = hurtObjEx.GetCurHP() % ChConfig.Def_PerPointValue
            hurtEx.CurHPEx = hurtObjEx.GetCurHP() / ChConfig.Def_PerPointValue
            hurtEx.SuckHP = min(hurtObjEx.GetSuckHP(), ChConfig.Def_UpperLimit_DWord)
            hurt.HurtListEx.append(hurtEx)
        hurt.HurtCountEx = len(hurt.HurtListEx)
        clientPack.HurtList.append(hurt)
    # 技能的额外伤害,如常规技能的平摊伤害
    clientPack.HurtListEx = []
    for hurtObjEx in useSkill.GetHurtObjListEx():
        hurtEx = poolMgr.acquire(ChPyNetSendPack.tagSCUseSkillHurtEx)
        hurtEx.ObjID = hurtObjEx.GetObjID()
        hurtEx.AttackTypes = hurtObjEx.GetHurtTypes()
        hurtEx.HurtHP = hurtObjEx.GetHurtHP() % ChConfig.Def_PerPointValue
        hurtEx.HurtHPEx = hurtObjEx.GetHurtHP() / ChConfig.Def_PerPointValue
        hurtEx.CurHP = hurtObjEx.GetCurHP() % ChConfig.Def_PerPointValue
        hurtEx.CurHPEx = hurtObjEx.GetCurHP() / ChConfig.Def_PerPointValue
        hurtEx.SuckHP = min(hurtObjEx.GetSuckHP(), ChConfig.Def_UpperLimit_DWord)
        clientPack.HurtListEx.append(hurtEx)
    clientPack.HurtCountEx = len(clientPack.HurtListEx)
    # 没有主要伤害时视为没有伤害,如纯buff的技能,需要通知目标给前端
    if not clientPack.HurtList:
        for tagObj in useSkill.GetTagObjList():
            tagID = tagObj.GetID()