hxp
2026-01-22 585920f6469cc24091ada7882c329d45142fd35f
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/TurnSkill.py
@@ -38,6 +38,11 @@
    ght = useSkill.GetHurtType() % 10
    if ght == IPY_GameWorld.ghtMag: # 做配置兼容用,优先验证法伤,否则默认物伤
        return IPY_GameWorld.ghtMag
    if ght == 3: # 由触发的技能决定
        bySkill = useSkill.GetBySkill()
        if bySkill:
            #GameWorld.DebugLogEx("物法类型取触发的技能: useSkillID=%s,bySkillID=%s", useSkill.GetSkillID(), bySkill.GetSkillID())
            return GetPMType(batObj, bySkill)
    return IPY_GameWorld.ghtPhy
def IsIgnoreDef(useSkill):
@@ -59,13 +64,27 @@
    if not skillID:
        return
    
    triggerWay = kwargs["triggerWay"] if "triggerWay" in kwargs else 0
    if not curBatObj.IsAlive():
        if useSkill.GetSkillType() == ChConfig.Def_SkillType_Revive and useSkill.GetTagAim() == ChConfig.SkillTagAim_Self:
            GameWorld.DebugLogEx("死亡时使用复活自己的技能! skillID=%s", skillID)
    skillType = useSkill.GetSkillType()
    byBatObj = kwargs.pop("byBatObj", None)
    if useSkill.GetFuncType() == ChConfig.Def_SkillFuncType_MinggeSkill:
        if useSkill.GetSkillType() in ChConfig.Def_HurtSkill_List:
            if not byBatObj:
                return
            GameWorld.DebugLogEx("命格攻击类技能施法方算触发者! mgObjID=%s,byObjID=%s,skillID=%s", curBatObj.GetID(), byBatObj.GetID(), skillID)
            curBatObj = byBatObj
        else:
            if triggerWay in ChConfig.DeadCanTriggerWayList:
                GameWorld.DebugLogEx("死亡可触发的方式触发技能可释放! skillID=%s,triggerWay=%s", skillID, triggerWay)
            # 因为命格攻击类技能修改了施法者,后续如果有子技能触发会变成还是变更后的施法者,导致命格全体buff可能存在多个施法来源,可能导致bug
            batLineup = curBatObj.GetTFBatLineup()
            curBatObj = batLineup.getMinggeObj()
            GameWorld.DebugLogEx("命格非攻击类技能强制施法者为命格自己! mgObjID=%s,skillID=%s", curBatObj.GetID(), skillID)
    byTriggerWay = kwargs["byTriggerWay"] if "byTriggerWay" in kwargs else 0 # 不使用参数名 triggerWay, 防止传递过程中参数混乱
    if not curBatObj.IsAlive():
        if skillType == ChConfig.Def_SkillType_Revive:
            GameWorld.DebugLogEx("死亡时使用复活技能! skillID=%s", skillID)
        else:
            if byTriggerWay in ChConfig.DeadCanTriggerWayList:
                GameWorld.DebugLogEx("死亡可触发的方式触发技能可释放! skillID=%s,byTriggerWay=%s", skillID, byTriggerWay)
            elif batType == ChConfig.TurnBattleType_Enhance:
                GameWorld.DebugLogEx("死亡状态额外技能不限制,主技能能放的话也就能放! skillID=%s", skillID)                
            else:
@@ -85,7 +104,7 @@
            GameWorld.DebugLogEx("技能使用处于buff状态限制中! skillID=%s,buffStateGroups=%s,limitState=%s", skillID, buffStateGroups, limitState)
            return
        
    if CheckSkillUseCntLimit(curBatObj, useSkill):
    if CheckSkillUseCntLimit(turnFight, curBatObj, useSkill):
        return
    
    #没有指定目标,则按技能自身的目标逻辑
@@ -103,10 +122,16 @@
            if not tagObjList and isRemove:
                return
            
    if tagObjList and skillType == ChConfig.Def_SkillType_Revive:
        for tagObj in tagObjList[::-1]:
            if tagObj.CheckInState(ChConfig.BatObjState_RebornLimit):
                GameWorld.DebugLogEx("    目标被禁止复活不处理! tagID=%s", tagObj.GetID())
                tagObjList.remove(tagObj)
    if not tagObjList:
        # 可扩展其他目标选择,如复活技能没有死亡单位时则使用另外的效果
        GameWorld.DebugLogEx("找不到技能目标! skillID=%s,mapID=%s,funcLineID=%s", skillID, turnFight.mapID, turnFight.funcLineID, turnFight.getReqPlayerID())
        if useSkill.GetSkillType() == ChConfig.Def_SkillType_Revive:
        if skillType == ChConfig.Def_SkillType_Revive and not isinstance(useSkill, IpyGameDataPY.IPY_Skill):
            otherEff = useSkill.GetEffectByID(ChConfig.SkillEff_ReviveNoUse)
            if otherEff:
                otherSkillID = otherEff.GetEffectValue(0)
@@ -126,19 +151,20 @@
    
    # 子技能怒气溢出值也有效,所以子技能不处理
    curXP = curBatObj.GetXP()
    if batType == ChConfig.TurnBattleType_Enhance:
        pass
    elif SkillCommon.isAngerSkill(useSkill):
    if SkillCommon.isAngerSkill(useSkill):
        maxXP = IpyGameDataPY.GetFuncCfg("AngerXP", 2)
        angerOverflow = max(curXP - maxXP, 0)
        curBatObj.SetAngerOverflow(angerOverflow)
        GameWorld.DebugLogEx("怒气溢出值: curXP=%s/%s,angerOverflow=%s", curXP, maxXP, angerOverflow)
    elif batType == ChConfig.TurnBattleType_Enhance:
        pass
    else:
        curBatObj.SetAngerOverflow(0)        
    angerOverflow = curBatObj.GetAngerOverflow()
    
    bySkillID = bySkill.GetSkillID() if bySkill else 0
    GameWorld.DebugLogEx("◆使用技能: curID=%s,skillID=%s,tagCnt=%s,batType=%s,bySkillID=%s,HP:%s/%s,angerOverflow=%s,curXP=%s,oneActionUseCnt=%s",
                         objID, skillID, len(tagObjList), batType, bySkillID, curBatObj.GetHP(), curBatObj.GetMaxHP(), angerOverflow, curXP, oneActionUseCnt)
    GameWorld.DebugLogEx("◆使用技能: skillID=%s,curID=%s,tagCnt=%s,batType=%s,bySkillID=%s,HP:%s/%s,angerOverflow=%s,curXP=%s,oneActionUseCnt=%s",
                         skillID, objID, len(tagObjList), batType, bySkillID, curBatObj.GetHP(), curBatObj.GetMaxHP(), angerOverflow, curXP, oneActionUseCnt)
    # 以下为技能可以使用的处理,之后的逻辑默认技能使用成功
    
    if isinstance(useSkill, IpyGameDataPY.IPY_Skill):
@@ -150,7 +176,9 @@
    useSkill.SetBatType(batType)
    useSkill.SetBySkill(bySkill)
    useSkill.SetByBuff(byBuff)
    useSkill.SetByTriggerWay(triggerWay)
    # 注:理论上原先的 byFriendObj 都可以直接用这个,但是旧的代码先不修改了,后续的累计敌军、友军之类的触发可以统一使用 byBatObj
    useSkill.SetByBatObj(kwargs.pop("byBatObj", None))
    useSkill.SetByTriggerWay(byTriggerWay)
    
    isTurnNormalSkill = SkillCommon.isTurnNormalSkill(useSkill)
    if isTurnNormalSkill:
@@ -177,9 +205,9 @@
    if SkillCommon.IsBuff(useSkill):
        __doAddBuff(turnFight, curBatObj, useSkill, **kwargs)
    else:
        __doUseSkill(turnFight, curBatObj, useSkill)
        __doUseSkill(turnFight, curBatObj, useSkill, **kwargs)
        
    DoAttackResult(turnFight, curBatObj, useSkill)
    DoAttackResult(turnFight, curBatObj, useSkill, **kwargs)
    
    if useTag:
        Sync_TurnFightTag(turnFight, useTag, 1)
@@ -193,9 +221,9 @@
    useSkill.ResetUseRec()
    return True
def CheckSkillUseCntLimit(batObj, useSkill):
def CheckSkillUseCntLimit(turnFight, batObj, useSkill):
    ## 检查技能使用次数是否受限
    # @return: 是否受限
    # @return: 是否受限,None-没有受限效果,False-不受限,True-受限了
    if not hasattr(useSkill, "GetEffectByID"):
        return
    effect = useSkill.GetEffectByID(ChConfig.SkillEff_SkillUseCntLimit)
@@ -206,22 +234,25 @@
    
    skillID = useSkill.GetSkillID()
    if useCntLimit:
        useCntLimit += TurnPassive.GetTriggerEffectValue(turnFight, batObj, None, ChConfig.PassiveEff_AddSkillUseCntLimit, useSkill, LimitByTurnAll=1)
        useCnt = batObj.GetSkillUseCnt(skillID)
        if useCnt >= useCntLimit:
            GameWorld.DebugLogEx("技能每场战斗使用次数受限! skillID=%s,useCnt=%s >= %s", skillID, useCnt, useCntLimit)
            return True
        
    if turnUseCntLimit:
        turnUseCntLimit += TurnPassive.GetTriggerEffectValue(turnFight, batObj, None, ChConfig.PassiveEff_AddSkillUseCntLimit, useSkill, LimitByTurnPer=1)
        turnUseCnt = batObj.GetSkillTurnUseCnt(skillID)
        if turnUseCnt >= turnUseCntLimit:
            GameWorld.DebugLogEx("技能每大回合使用次数受限! skillID=%s,turnUseCnt=%s >= %s", skillID, turnUseCnt, turnUseCntLimit)
            return True
        
    return
    return False
def IsNeedSyncSkill(useSkill):
    ## 使用需要同步B427使用技能
    if useSkill.GetSkillType() in [ChConfig.Def_SkillType_Atk, ChConfig.Def_SkillType_Cure, ChConfig.Def_SkillType_Revive, ChConfig.Def_SkillType_CleanBuff]:
    if useSkill.GetSkillType() in [ChConfig.Def_SkillType_Atk, ChConfig.Def_SkillType_Cure, ChConfig.Def_SkillType_Revive, ChConfig.Def_SkillType_CleanBuff,
                                   ChConfig.Def_SkillType_AtkEx]:
        return True
    if useSkill.GetFuncType() in [ChConfig.Def_SkillFuncType_TurnNormaSkill, ChConfig.Def_SkillFuncType_AngerSkill]:
        return True
@@ -367,13 +398,14 @@
                    colNumList.insert(0, specInColNum)
                    
            GameWorld.DebugLogEx("纵排: colNumList=%s,specObjID-PosNum=%s-%s", colNumList, specObjID, specObjPosNum)
            posObjIDDict = batLineup.getPosObjIDDict()
            for col in colNumList:
                for row in range(1, 1 + ChConfig.TurnFightRows):
                    pNum = (row - 1) * ChConfig.TurnFightCols + col
                    #GameWorld.DebugLogEx("    col=%s,row=%s,pNum=%s", col, row, pNum)
                    if pNum not in batLineup.posObjIDDict:
                    if pNum not in posObjIDDict:
                        continue
                    tagObjID = batLineup.posObjIDDict[pNum]
                    tagObjID = posObjIDDict[pNum]
                    tagBatObj = batObjMgr.getBatObj(tagObjID)
                    if not __skillTagFilter(curBatObj, tagBatObj, tagAffect, isNoSelf):
                        continue
@@ -392,16 +424,24 @@
                
            GameWorld.DebugLogEx("全部: colNumList=%s,specObjID-PosNum=%s-%s", colNumList, specObjID, specObjPosNum)
            # 按前排优先原则
            posObjIDDict = batLineup.getPosObjIDDict()
            for row in range(1, 1 + ChConfig.TurnFightRows):
                for col in colNumList:
                    pNum = (row - 1) * ChConfig.TurnFightCols + col
                    #GameWorld.DebugLogEx("    col=%s,row=%s,pNum=%s", col, row, pNum)
                    if pNum not in batLineup.posObjIDDict:
                    if pNum not in posObjIDDict:
                        continue
                    tagObjID = batLineup.posObjIDDict[pNum]
                    tagObjID = posObjIDDict[pNum]
                    tagBatObj = batObjMgr.getBatObj(tagObjID)
                    if not __skillTagFilter(curBatObj, tagBatObj, tagAffect, isNoSelf):
                        continue
                    if tagAim == ChConfig.SkillTagAim_Male:
                        if tagBatObj.GetSex() != ChConfig.BatObjSex_Male:
                            continue
                    elif tagAim == ChConfig.SkillTagAim_Female:
                        if tagBatObj.GetSex() != ChConfig.BatObjSex_Female:
                            continue
                    aimObjList.append(tagBatObj)
                    
    # 目标细分
@@ -409,6 +449,11 @@
    if tagAffect == ChConfig.SkillTagAffect_HPLowest:
        aimObjList.sort(key=lambda o:(o.GetHP()), reverse=False)
        #GameWorld.DebugLogEx("血量最低排序: %s", [[o.GetID(), o.GetHP(), o.GetMaxHP()] for o in aimObjList])
    # 血量百分比最低
    elif tagAffect == ChConfig.SkillTagAffect_HPPerLowest:
        aimObjList.sort(key=lambda o:(o.GetHP() / float(o.GetMaxHP())), reverse=False)
        #GameWorld.DebugLogEx("血量百分比最低排序: %s", [[o.GetID(), o.GetHP(), o.GetMaxHP(), o.GetHP() / float(o.GetMaxHP())] for o in aimObjList])
        
    # 血量最高
    elif tagAffect == ChConfig.SkillTagAffect_HPHighest:
@@ -442,6 +487,14 @@
        for aimObj in aimObjList[::-1]:
            if not aimObj.CheckInState(ChConfig.BatObjState_PoisonCure):
                aimObjList.remove(aimObj)
    # 仅流血目标
    elif tagAffect == ChConfig.SkillTagAffect_Bleeding:
        atkBackTagFrist = False
        for aimObj in aimObjList[::-1]:
            if not aimObj.CheckInState(ChConfig.BatObjState_Bleeding):
                aimObjList.remove(aimObj)
    else:
        # 范围目标超过个数,则随机取
        if tagCount and len(aimObjList) > tagCount:
@@ -510,7 +563,7 @@
    batObjMgr = BattleObj.GetBatObjMgr()
    for num in lineupNumList:
        batLineup = batFaction.getBatlineup(num)
        for tagID in batLineup.posObjIDDict.values():
        for tagID in batLineup.getBatHeroObjIDList():
            tagObj = batObjMgr.getBatObj(tagID)
            if tagObj.IsAlive() and tagObj.CheckInState(checkInStates):
                return lineupNum, changeTagSet
@@ -546,19 +599,80 @@
        
    GameWorld.DebugLogEx("前后排: rowNumList=%s,colNumList=%s,specObjID-PosNum=%s-%s", rowNumList, colNumList, specObjID, specObjPosNum)
    aimObjList = []
    posObjIDDict = batLineup.getPosObjIDDict()
    for row in rowNumList:
        for col in colNumList:
            pNum = (row - 1) * ChConfig.TurnFightCols + col
            #GameWorld.DebugLogEx("    row=%s,col=%s,pNum=%s", row, col, pNum)
            if pNum not in batLineup.posObjIDDict:
            if pNum not in posObjIDDict:
                continue
            tagObjID = batLineup.posObjIDDict[pNum]
            tagObjID = posObjIDDict[pNum]
            tagBatObj = batObjMgr.getBatObj(tagObjID)
            if not __skillTagFilter(curBatObj, tagBatObj, tagAffect, isNoSelf):
                continue
            aimObjList.append(tagBatObj)
        if aimObjList:
            break
    return aimObjList
def __getSameRowObjList(tagObj):
    ## 获取目标对应的同排对象列表,只算活着的
    posNum = tagObj.GetPosNum()
    inRowNum = ChConfig.GetInRowNum(posNum)
    inColNum = ChConfig.GetInColNum(posNum)
    # 优先目标所在纵,为主目标
    colNumList = range(1, 1 + ChConfig.TurnFightCols)
    if inColNum in colNumList:
        colNumList.remove(inColNum)
        colNumList.insert(0, inColNum)
    row = inRowNum # 目标玩家所在横排
    batObjMgr = BattleObj.GetBatObjMgr()
    batLineup = tagObj.GetTFBatLineup()
    aimObjList = []
    posObjIDDict = batLineup.getPosObjIDDict()
    for col in colNumList:
        pNum = (row - 1) * ChConfig.TurnFightCols + col
        if pNum not in posObjIDDict:
            continue
        tagObjID = posObjIDDict[pNum]
        tagBatObj = batObjMgr.getBatObj(tagObjID)
        if not tagBatObj.IsAlive():
            continue
        aimObjList.append(tagBatObj)
    return aimObjList
def __getSameColObjList(tagObj):
    ## 获取目标对应的同列对象列表,只算活着的
    posNum = tagObj.GetPosNum()
    inRowNum = ChConfig.GetInRowNum(posNum)
    inColNum = ChConfig.GetInColNum(posNum)
    rowNumList = range(1, 1 + ChConfig.TurnFightRows)
    if inRowNum in rowNumList:
        rowNumList.remove(inRowNum)
        rowNumList.insert(0, inRowNum)
    col = inColNum # 目标玩家所在纵排
    batObjMgr = BattleObj.GetBatObjMgr()
    batLineup = tagObj.GetTFBatLineup()
    aimObjList = []
    posObjIDDict = batLineup.getPosObjIDDict()
    for row in range(1, 1 + ChConfig.TurnFightRows):
        pNum = (row - 1) * ChConfig.TurnFightCols + col
        if pNum not in posObjIDDict:
            continue
        tagObjID = posObjIDDict[pNum]
        tagBatObj = batObjMgr.getBatObj(tagObjID)
        if not tagBatObj.IsAlive():
            continue
        aimObjList.append(tagBatObj)
        
    return aimObjList
@@ -606,12 +720,13 @@
        colNumList.insert(0, inColNum)
        
    # 按前排优先原则
    posObjIDDict = batLineup.getPosObjIDDict()
    for row in range(ChConfig.TurnFightRows):
        for col in colNumList:
            pNum = row * ChConfig.TurnFightCols + col
            if pNum not in batLineup.posObjIDDict:
            if pNum not in posObjIDDict:
                continue
            tagObjID = batLineup.posObjIDDict[pNum]
            tagObjID = posObjIDDict[pNum]
            tagBatObj = batObjMgr.getBatObj(tagObjID)
            if not __skillTagFilter(curBatObj, tagBatObj, tagAffect, isNoSelf=True):
                continue
@@ -655,7 +770,7 @@
        
    return
def __doUseSkill(turnFight, curBatObj, useSkill):
def __doUseSkill(turnFight, curBatObj, useSkill, **kwargs):
    
    atkType = useSkill.GetAtkType()
    GameWorld.DebugLogEx("__doUseSkill: curID=%s,skillID=%s,atkType=%s", curBatObj.GetID(), useSkill.GetSkillID(), atkType)
@@ -665,7 +780,7 @@
    
    # 通用攻击
    if atkType == 1:
        SkillModule_1(turnFight, curBatObj, useSkill)
        SkillModule_1(turnFight, curBatObj, useSkill, **kwargs)
    # 治疗
    if atkType == 2:
        SkillModule_2(turnFight, curBatObj, useSkill)
@@ -732,9 +847,13 @@
        return
    harmPer = harmEff.GetEffectValue(0) # 自残百分比
    noEnoughDo = harmEff.GetEffectValue(1) # 不足时扣除方式 0-不扣;1-自杀;2-扣剩1点
    calcType = harmEff.GetEffectValue(2) # 计算方式:0-按最大血量;1-按当前血量
    curHP = curBatObj.GetHP()
    maxHP = curBatObj.GetMaxHP()
    harmHP = int(maxHP * harmPer / 100.0)
    if calcType == 1:
        harmHP = int(curHP * harmPer / 100.0)
    else:
        harmHP = int(maxHP * harmPer / 100.0)
    lostHP = harmHP
    if curHP <= harmHP:
        if noEnoughDo == 0:
@@ -744,8 +863,8 @@
            
    updHP = max(curHP - lostHP, 0)
    curBatObj.SetHP(updHP, False)
    GameWorld.DebugLogEx("使用技能时自残: curHP=%s/%s,harmPer=%s,harmHP=%s,lostHP=%s,updHP=%s,noEnoughDo=%s",
                         curHP, maxHP, harmPer, harmHP, lostHP, updHP, noEnoughDo)
    GameWorld.DebugLogEx("使用技能时自残: curHP=%s/%s,harmPer=%s,harmHP=%s,lostHP=%s,updHP=%s,noEnoughDo=%s,calcType=%s",
                         curHP, maxHP, harmPer, harmHP, lostHP, updHP, noEnoughDo, calcType)
    
    curBatObj.SetHarmSelfHP(harmHP) # 无视实际扣血量,直接更新
    
@@ -756,7 +875,7 @@
    Sync_PropertyRefreshView(turnFight, curBatObj, ChConfig.AttrID_HP, updHP, diffValue, diffType, skillID, relatedSkillID, hurtTypes)
    return
def SkillModule_1(turnFight, curBatObj, useSkill):
def SkillModule_1(turnFight, curBatObj, useSkill, **kwargs):
    ## 通用攻击,单攻、群攻
    
    addPer = 0
@@ -772,22 +891,27 @@
            addPer = addPerMax
        GameWorld.DebugLogEx("司马懿特殊潜能技能额外增加比例: frozenCnt=%s,addPer=%s" % (frozenCnt, addPer))
        
    addBatDamPer = 0
    if "addBatDamPer" in kwargs:
        addBatDamPer += kwargs.pop("addBatDamPer", 0)
    addBatDamPer += TurnPassive.GetTriggerEffectValue(turnFight, curBatObj, None, ChConfig.PassiveEff_AddBatDamPerByLayer, useSkill)
    # 计算伤害
    calcHurtResults = []
    for tagBatObj in useSkill.GetTagObjList():
        hurtValue, hurtTypes, immuneHurt = __calcSkillHurt(turnFight, curBatObj, tagBatObj, useSkill, addPer)
        hurtValue, hurtTypes, immuneHurt = __calcSkillHurt(turnFight, curBatObj, tagBatObj, useSkill, addPer, addBatDamPer)
        calcHurtResults.append([tagBatObj, hurtValue, hurtTypes, immuneHurt])
        
    DoSkillHurtHP(turnFight, curBatObj, useSkill, calcHurtResults)        
    return
def __calcSkillHurt(turnFight, atkObj, defObj, curSkill, addPer=0):
def __calcSkillHurt(turnFight, atkObj, defObj, curSkill, addPer=0, addBatDamPer=0):
    ## 计算技能伤害,只计算值,不做实际处理
    # @param addPer: 外部指定增加的比例
    # @return: hurtValue, hurtTypes, immuneHurt
    atkSkillPer = curSkill.GetSkillPer() + addPer
    atkSkillValue = curSkill.GetSkillValue()
    hurtValue, hurtTypes = CalcFormatHurt(turnFight, atkObj, defObj, curSkill, atkSkillValue, atkSkillPer)
    hurtValue, hurtTypes = CalcFormatHurt(turnFight, atkObj, defObj, curSkill, atkSkillValue, atkSkillPer, addBatDamPer)
    hurtValue, hurtTypes, immuneHurt = CalcHurtWithBuff(turnFight, atkObj, defObj, hurtValue, hurtTypes)
    return hurtValue, hurtTypes, immuneHurt
@@ -802,7 +926,7 @@
        tagID = tagObj.GetID()
        if tagID == ChConfig.HeroID_Caoren:
            continue
        if tagObj.CheckInState(ChConfig.BatObjState_Wudi):
        if tagObj.CheckInState(ChConfig.ImmuneHurtStateList):
            continue
        if not caorenProtectObj or tagObj.GetHP() < caorenProtectObj.GetHP():
            caorenProtectObj = tagObj
@@ -880,7 +1004,7 @@
            continue
        if tagID == ChConfig.HeroID_Caoren:
            continue
        if tagObj.CheckInState(ChConfig.BatObjState_Wudi):
        if tagObj.CheckInState(ChConfig.ImmuneHurtStateList):
            continue
        if not caorenProtectObj or tagObj.GetHP() < caorenProtectObj.GetHP():
            caorenProtectObj = tagObj
@@ -1106,7 +1230,7 @@
        else:
            diffType = 1
            tagXP = tagBatObj.GetXP()
            diffValue = GetEnhanceXP(tagBatObj, calcValue)
            diffValue = GetEnhanceXP(tagBatObj, calcValue, useSkill)
            updValue = tagXP + diffValue
            tagBatObj.SetXP(updValue, False)
            GameWorld.DebugLogEx("    加怒气: tagID=%s,diffValue=%s,tagXP=%s,updXP=%s", tagID, diffValue, tagXP, updValue)
@@ -1123,7 +1247,7 @@
        
    return
def DoAttackResult(turnFight, curBatObj, useSkill):
def DoAttackResult(turnFight, curBatObj, useSkill, **kwargs):
    '''执行攻击结果
    @param curBatObj: 施法方或buff归属方
    '''
@@ -1140,13 +1264,21 @@
    if useSkill.GetCoolDownTime():
        useSkill.SetRemainTime(useSkill.GetCoolDownTime())
        
    if "byEffSkill" in kwargs:
        byEffSkill = kwargs["byEffSkill"]
        cdTime = byEffSkill.GetCoolDownTime()
        if cdTime:
            if hasattr(byEffSkill, "GetRemainTime"):
                byEffSkill.SetRemainTime(cdTime)
                GameWorld.DebugLogEx("提前设置效果技能触发后CD,防止嵌套循环触发! effSkillID=%s,cdTime=%s", byEffSkill.GetSkillID(), cdTime)
    __doCostZhanchui(turnFight, curBatObj, useSkill) # 先扣除再同步技能
    
    # 需先技能使用 - 前端按顺序优先表现技能释放内容,前端需要动作或有伤血则通知
    if IsNeedSyncSkill(useSkill):
        Sync_UseSkill(turnFight, curBatObj, useSkill)
        
    __doSkillUserAnger(turnFight, curBatObj, useSkill)
    __doSkillUserAnger(turnFight, curBatObj, useSkill, **kwargs)
    
    DoBeAttackResult(turnFight, curBatObj, useSkill)
    return
@@ -1273,13 +1405,14 @@
            return useSkill
    return
def DoBeAttackResult(turnFight, curObj, useSkill, curBuff=None):
def DoBeAttackResult(turnFight, curObj, useSkill, timeBuff=None):
    '''被攻击结果
    @param curObj: 施法方或buff归属方
    @param timeBuff: 持续结算的buff,如持续攻击、持续治疗等
    '''
    
    #curID = curObj.GetID()
    isUseSkill = False if curBuff else True # buff的视为持续性的,否则为直接使用技能的攻击结果
    curID = curObj.GetID()
    isUseSkill = False if timeBuff else True # buff的视为持续性的,否则为直接使用技能的攻击结果
    isTurnNormalSkill = SkillCommon.isTurnNormalSkill(useSkill)
    isAngerSkill = SkillCommon.isAngerSkill(useSkill)
    
@@ -1288,6 +1421,8 @@
    # 优先处理afterLogic,可再预先汇总一些会触发被动的信息
    relatedSkillID = useSkill.GetSkillID()
    delBuffAfterEffList = [] # buff消失后要触发的被动,一般用于后置处理的逻辑
    beControlledHardDict = {} # 受控目标 {objID:buff, ...}
    beDotBuffDict = {} # 被添加dot的目标 {objID:buff, ...}
    afterLogicList = useSkill.GetAfterLogicList()
    for logicType, logicData in afterLogicList:
        if logicType == ChConfig.AfterLogic_DelBuff:
@@ -1306,37 +1441,55 @@
            buff = logicData[1]
            TurnBuff.SyncBuffRefresh(turnFight, buffObj, buff, relatedSkillID)
            
        # 统计添加buff需要触发的被动
        elif logicType == ChConfig.AfterLogic_TriggerAddBuff:
            triggerType, batObj, buff, buffOwner = logicData
            if buffOwner.GetID() == curID:
                if triggerType == ChConfig.TriggerWay_BeControlledHard:
                    beControlledHardDict[batObj.GetID()] = buff
                elif triggerType == ChConfig.TriggerWay_AddDOTBuff:
                    beDotBuffDict[batObj.GetID()] = buff
    # 统计击杀
    killObjList = [] # 击杀其他阵营目标列表
    dieObjList = [] # 死亡的单位列表,包含友方单位或自己
    nodeadObjList = [] # 不死的单位列表
    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)
            if tagObj.CanNoDead():
                tagObj.SetHP(1, True) # 触发不死,保留1点血量
                nodeadObjList.append(tagObj)
            else:
                dieObjList.append(tagObj)
                if tagObj.GetFaction() != curObj.GetFaction():
                    killObjList.append(tagObj)
                TurnAttack.SetObjKilled(turnFight, tagObj, curObj, useSkill)
    useSkill.SetKillObjList(killObjList)
    # 判断自己,因为反弹、平摊伤害的原因,有可能自己干死自己
    if curObj.IsAlive() and curObj.GetHP() <= 0:
        dieObjList.append(curObj)
        TurnAttack.SetObjKilled(turnFight, curObj)
    # 统计伤血,可能单个技能对同一目标造成多次伤害
    totalHurtValue = 0
    isSuckHP = False
    suckObjIDList = [] # 吸血的对象ID列表
    missObjIDList, immuneObjIDList = [], [] # 闪避、免疫对象ID列表
    stunObjIDList, superHitObjIDList, parryObjIDList = [], [], []
    beHurtObjIDList = [] # 受伤的对象ID列表
    reviveObjList = [] # 复活的对象列表
    bounceObjList = [] # 有反弹的对象列表
    caorenProtectList = [] # 有被曹仁防护的对象列表
    for hurtObj in useSkill.GetHurtObjListAll():
        hurtObjID = hurtObj.GetObjID()
        tagObj = batObjMgr.getBatObj(hurtObjID)
        if not tagObj:
            continue
        bounceHP = hurtObj.GetBounceHP()
        if bounceHP:
            if tagObj not in bounceObjList:
                bounceObjList.append(tagObj)
        if hurtObj.HaveHurtType(ChConfig.HurtAtkType_Hurt) and not hurtObj.HaveHurtType(ChConfig.HurtAtkType_Immune) \
            and (isTurnNormalSkill or isAngerSkill) and tagObj.IsAlive():
            __doSkillHurtAnger(tagObj, hurtObj.GetLostHP(), useSkill)
@@ -1373,9 +1526,24 @@
            if hurtObjID not in stunObjIDList:
                stunObjIDList.append(hurtObjID)
                
        if hurtObj.GetSuckHP() > 0:
            isSuckHP = True
        if hurtObj.HaveHurtType(ChConfig.HurtAtkType_CaorenProtect):
            if tagObj not in caorenProtectList:
                caorenProtectList.append(tagObj)
        if hurtObj.GetSuckHP() > 0 and not hurtObj.HaveHurtType(ChConfig.HurtAtkType_PoisonCureSuck):
            if hurtObjID not in suckObjIDList:
                suckObjIDList.append(hurtObjID)
    # 判断自己死亡,因为反弹、平摊伤害的原因,有可能自己干死自己
    if curObj.IsAlive() and curObj.GetHP() <= 0:
        if curObj.CanNoDead():
            curObj.SetHP(1, True) # 触发不死,保留1点血量
            nodeadObjList.append(curObj)
        else:
            dieObjList.append(curObj)
            killer = bounceObjList[0] if bounceObjList else None
            TurnAttack.SetObjKilled(turnFight, curObj, killer)
    # 记录最后一次总伤害,有伤害目标才记录
    if totalHurtValue:
        curObj.SetLastHurtValue(totalHurtValue)
@@ -1385,7 +1553,7 @@
        DoHeroSpecialty(turnFight, curObj, ChConfig.HeroSpecialty_SuperHit, relatedSkillID)
    if stunObjIDList:
        DoHeroSpecialty(turnFight, curObj, ChConfig.HeroSpecialty_Stun, relatedSkillID)
    if isSuckHP:
    if suckObjIDList:
        DoHeroSpecialty(turnFight, curObj, ChConfig.HeroSpecialty_SuckHP, relatedSkillID)
        
    # 结算副本相关的攻击结果,仅主动发起玩家阵容武将触发
@@ -1400,6 +1568,10 @@
        __DoCurSkillEff(turnFight, curObj, useSkill, effIgnoreObjIDList, isUseSkill)
    
    # ========== 以下触发被动 ==========
    curBatLineup = curObj.GetTFBatLineup()
    curMGObj = curBatLineup.getMinggeObj()
    mgTriggerWayList = [] # 命格本次技能已触发方式列表,命格固定只触发一次
    
    # buff消失后置处理的被动,优先处理,如破盾,区别于被动方式  ChConfig.TriggerWay_BuffDel(立即触发)
    for buffObjID, buff, tagObjID, afterTriggerData in delBuffAfterEffList:
@@ -1424,18 +1596,22 @@
            batFaction = turnFight.getBatFaction(faction)
            for lineupNum in batFaction.lineupDict.keys():
                batLineup = batFaction.getBatlineup(lineupNum)
                for lineupObjID in batLineup.posObjIDDict.values():
                for lineupObjID in batLineup.getBatHeroObjIDList():
                    lineupObj = batObjMgr.getBatObj(lineupObjID)
                    if not lineupObj.IsAlive():
                        continue
                    for dieObj in dieObjList:
                        # 敌方单位死亡时
                        if lineupObj.GetFaction() != dieObj.GetFaction():
                            TurnPassive.OnTriggerPassiveEffect(turnFight, lineupObj, ChConfig.TriggerWay_EnemyDie, dieObj, connSkill=useSkill)
                            TurnPassive.OnTriggerPassiveEffect(turnFight, lineupObj, ChConfig.TriggerWay_EnemyDie, dieObj, connSkill=useSkill, byKiller=curObj)
                        # 己方单位死亡时
                        else:
                            TurnPassive.OnTriggerPassiveEffect(turnFight, lineupObj, ChConfig.TriggerWay_FriendDie, dieObj, connSkill=useSkill)
                            TurnPassive.OnTriggerPassiveEffect(turnFight, lineupObj, ChConfig.TriggerWay_FriendDie, dieObj, connSkill=useSkill, byFriendObj=dieObj)
    # 触发不死
    for tagObj in nodeadObjList:
        TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_NoDead, curObj, connSkill=useSkill)
    # 复活时
    for tagObj in reviveObjList:
        # 同步最新的怒气及buff,血量已经在技能中通知了
@@ -1443,7 +1619,24 @@
        TurnBuff.DoBuffByRevive(turnFight, tagObj)
        TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_Revive, curObj, connSkill=useSkill)
        TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_InBattlefield, curObj, connSkill=useSkill)
    if reviveObjList and curMGObj:
        tagObj = reviveObjList[0]
        TurnPassive.OnTriggerPassiveEffect(turnFight, curMGObj, ChConfig.TriggerWay_Revive, tagObj, connSkill=useSkill, byFriendObj=curObj)
        
    # 曹仁防护触发,暂写死
    for tagObj in caorenProtectList:
        tagBatLineup = tagObj.GetTFBatLineup()
        caorenObj = tagBatLineup.getHeroObj(ChConfig.HeroID_Caoren) # 获取被防护目标阵容的曹仁
        if not caorenObj or not caorenObj.IsAlive():
            continue
        caoRenSkill = caorenObj.GetSkillManager().FindSkillByID(ChConfig.SkillID_CaorenProtect)
        if not caoRenSkill:
            continue
        passiveSkillID = caoRenSkill.GetEffect(0).GetEffectValue(0) # 防护成功后的触发技能ID,默认写在效果1的值
        GameWorld.DebugLogEx("曹仁防护后触发技能: caorenID=%s,protectID=%s,passiveSkillID=%s", caorenObj.GetID(), tagObj.GetID(), passiveSkillID)
        if passiveSkillID:
            OnUsePassiveSkill(turnFight, caorenObj, curObj, passiveSkillID, connSkill=useSkill)
    triggerOne = False
    batType = useSkill.GetBatType()
    isAttackDirect = (isUseSkill and SkillCommon.isAttackDirectSkill(useSkill)) # 是否直接攻击
@@ -1454,6 +1647,14 @@
        if tagID in missObjIDList:
            TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_BeMiss, tagObj, connSkill=useSkill)
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_Miss, curObj, connSkill=useSkill)
            tagMGObj = tagObj.GetTFBatLineup().getMinggeObj()
            if tagMGObj and ChConfig.TriggerWay_Miss not in mgTriggerWayList:
                mgTriggerWayList.append(ChConfig.TriggerWay_Miss)
                TurnPassive.OnTriggerPassiveEffect(turnFight, tagMGObj, ChConfig.TriggerWay_Miss, curObj, connSkill=useSkill, byBatObj=tagObj)
        # 免疫
        if tagID in immuneObjIDList:
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_ImmuneHurt, curObj, connSkill=useSkill)
            
        if tagID in effIgnoreObjIDList:
            continue
@@ -1463,6 +1664,7 @@
        # 掉血时
        if tagID in beHurtObjIDList:
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeHurt, curObj, connSkill=useSkill)
            TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_HurtTag, tagObj, connSkill=useSkill)
            
        #  受到任意效果时(除直接攻击外的任意效果,如buff、dot、治疗、额外怒技)
        if not isAttackDirect:
@@ -1472,6 +1674,7 @@
        if isExObj:
            continue
        
        isDotHurt = False
        # 直接攻击
        if isAttackDirect:
            if not triggerOne:
@@ -1482,7 +1685,9 @@
        elif not isAttackDirect:
            # 受到持续伤害
            if tagID in beHurtObjIDList:
                TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeDOTHurt, curObj, connSkill=useSkill)
                isDotHurt = True
                TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_DOTHurt, tagObj, connSkill=useSkill, connBuff=timeBuff)
                TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeDOTHurt, curObj, connSkill=useSkill, connBuff=timeBuff)
                
        # 使用技能后
        if isUseSkill:
@@ -1494,30 +1699,102 @@
        if tagID in stunObjIDList:
            TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_Stun, tagObj, connSkill=useSkill)
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeStun, curObj, connSkill=useSkill)
            if curMGObj and ChConfig.TriggerWay_Stun not in mgTriggerWayList:
                mgTriggerWayList.append(ChConfig.TriggerWay_Stun)
                TurnPassive.OnTriggerPassiveEffect(turnFight, curMGObj, ChConfig.TriggerWay_Stun, tagObj, connSkill=useSkill, byBatObj=curObj)
        # 控制
        if tagID in beControlledHardDict:
            buff = beControlledHardDict[tagID]
            if buff.GetCurBuffState() == ChConfig.BatObjState_Frozen:
                if curMGObj and ChConfig.TriggerWay_Frozen not in mgTriggerWayList:
                    mgTriggerWayList.append(ChConfig.TriggerWay_Frozen)
                    TurnPassive.OnTriggerPassiveEffect(turnFight, curMGObj, ChConfig.TriggerWay_Frozen, tagObj, connSkill=useSkill, byBatObj=curObj)
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeControlledHard, curObj, connSkill=useSkill, connBuff=buff)
            
        # 附加dot
        if tagID in beDotBuffDict:
            if curMGObj and ChConfig.TriggerWay_AddDOTBuff not in mgTriggerWayList:
                mgTriggerWayList.append(ChConfig.TriggerWay_AddDOTBuff)
                TurnPassive.OnTriggerPassiveEffect(turnFight, curMGObj, ChConfig.TriggerWay_AddDOTBuff, tagObj, connSkill=useSkill, byBatObj=curObj)
        # 暴击
        if tagID in superHitObjIDList:
            TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_SuperHit, tagObj, connSkill=useSkill)
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeSuperHit, curObj, connSkill=useSkill)
            if curMGObj and ChConfig.TriggerWay_SuperHit not in mgTriggerWayList:
                mgTriggerWayList.append(ChConfig.TriggerWay_SuperHit)
                TurnPassive.OnTriggerPassiveEffect(turnFight, curMGObj, ChConfig.TriggerWay_SuperHit, tagObj, connSkill=useSkill, byBatObj=curObj)
        # 格挡
        if tagID in parryObjIDList:
            TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_BeParry, tagObj, connSkill=useSkill)
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_Parry, curObj, connSkill=useSkill)
            tagMGObj = tagObj.GetTFBatLineup().getMinggeObj()
            if tagMGObj and ChConfig.TriggerWay_Parry not in mgTriggerWayList:
                mgTriggerWayList.append(ChConfig.TriggerWay_Parry)
                TurnPassive.OnTriggerPassiveEffect(turnFight, tagMGObj, ChConfig.TriggerWay_Parry, curObj, connSkill=useSkill, byBatObj=tagObj)
        # 吸血
        if tagID in suckObjIDList:
            if not triggerOne:
                TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_SuckHPOne, tagObj, connSkill=useSkill)
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeSuckHP, curObj, connSkill=useSkill)
            if curMGObj and ChConfig.TriggerWay_SuckHPOne not in mgTriggerWayList:
                mgTriggerWayList.append(ChConfig.TriggerWay_SuckHPOne)
                TurnPassive.OnTriggerPassiveEffect(turnFight, curMGObj, ChConfig.TriggerWay_SuckHPOne, tagObj, connSkill=useSkill, byBatObj=curObj)
        # 连击
        if batType == ChConfig.TurnBattleType_Combo:
            TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_Combo, tagObj, connSkill=useSkill)
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeCombo, curObj, connSkill=useSkill)
            if curMGObj and ChConfig.TriggerWay_Combo not in mgTriggerWayList:
                mgTriggerWayList.append(ChConfig.TriggerWay_Combo)
                TurnPassive.OnTriggerPassiveEffect(turnFight, curMGObj, ChConfig.TriggerWay_Combo, tagObj, connSkill=useSkill, byBatObj=curObj)
        # 追击
        elif batType == ChConfig.TurnBattleType_Pursue:
            TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_Pursue, tagObj, connSkill=useSkill)
            if isAttackDirect:
                if curMGObj and ChConfig.TriggerWay_PursueAtk not in mgTriggerWayList:
                    mgTriggerWayList.append(ChConfig.TriggerWay_PursueAtk)
                    TurnPassive.OnTriggerPassiveEffect(turnFight, curMGObj, ChConfig.TriggerWay_PursueAtk, tagObj, connSkill=useSkill, byBatObj=curObj)
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BePursue, curObj, connSkill=useSkill)
        # 反击
        elif batType == ChConfig.TurnBattleType_AtkBack:
            TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_AtkBack, tagObj, connSkill=useSkill)
            TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeAtkBack, curObj, connSkill=useSkill)
            
        # 敌友方
        if isAttackDirect or batType in [ChConfig.TurnBattleType_Combo, ChConfig.TurnBattleType_Pursue] or isDotHurt or tagID in beControlledHardDict:
            for lineupObjID in curBatLineup.getBatHeroObjIDList():
                lineupObj = batObjMgr.getBatObj(lineupObjID)
                if not lineupObj.IsAlive():
                    continue
                # 直接攻击
                if isAttackDirect:
                    if not triggerOne:
                        TurnPassive.OnTriggerPassiveEffect(turnFight, lineupObj, ChConfig.TriggerWay_FriendAttackOverDirectOne, tagObj, connSkill=useSkill, byFriendObj=curObj)
                        if curID != lineupObj.GetID():
                            TurnPassive.OnTriggerPassiveEffect(turnFight, lineupObj, ChConfig.TriggerWay_FriendAttackOverDirectOneNoSelf, tagObj, connSkill=useSkill, byFriendObj=curObj)
                # 持续伤害
                elif isDotHurt:
                    if not triggerOne:
                        TurnPassive.OnTriggerPassiveEffect(turnFight, lineupObj, ChConfig.TriggerWay_FriendDotHurt, tagObj, connSkill=useSkill, byFriendObj=curObj)
                # 敌方被控时
                if tagID in beControlledHardDict and lineupObj.GetFaction() != tagObj.GetFaction():
                    TurnPassive.OnTriggerPassiveEffect(turnFight, lineupObj, ChConfig.TriggerWay_EnemyBeControlledHard, tagObj, connSkill=useSkill)
                # 连击
                if batType == ChConfig.TurnBattleType_Combo:
                    TurnPassive.OnTriggerPassiveEffect(turnFight, lineupObj, ChConfig.TriggerWay_FriendCombo, tagObj, connSkill=useSkill, byFriendObj=curObj)
                # 追击
                elif batType == ChConfig.TurnBattleType_Pursue:
                    TurnPassive.OnTriggerPassiveEffect(turnFight, lineupObj, ChConfig.TriggerWay_FriendPursue, tagObj, connSkill=useSkill, byFriendObj=curObj)
        triggerOne = True # 设置已经触发过一次
        
    # 验证是否结算,最后处理
@@ -1534,9 +1811,7 @@
    if not batFaction:
        return
    batLineup = batFaction.getBatlineup(1)
    if ChConfig.HeroID_Dongbai not in batLineup.heroObjIDDict:
        return
    dongbaiObj = BattleObj.GetBatObjMgr().getBatObj(batLineup.heroObjIDDict[ChConfig.HeroID_Dongbai])
    dongbaiObj = batLineup.getHeroObj(ChConfig.HeroID_Dongbai)
    if not dongbaiObj or dongbaiObj.IsAlive():
        return
    skill = dongbaiObj.GetSkillManager().FindSkillByID(ChConfig.SkillID_DongbaiRevive)
@@ -1590,10 +1865,14 @@
        
    return
def __doSkillUserAnger(turnFight, curBatObj, useSkill):
def __doSkillUserAnger(turnFight, curBatObj, useSkill, **kwargs):
    ## 技能释放者怒气相关
    if SkillCommon.isAngerSkill(useSkill):
        curBatObj.SetXP(0)
        if "noUseXP" in kwargs:
            kwargs.pop("noUseXP")
            GameWorld.DebugLogEx("本次不消耗怒气")
        else:
            curBatObj.SetXP(0)
    elif SkillCommon.isTurnNormalSkill(useSkill) and useSkill.GetBatType() == ChConfig.TurnBattleType_Normal:
        addXP = IpyGameDataPY.GetFuncCfg("AngerXP", 3)
        AddFightXP(curBatObj, addXP, "skillID:%s" % useSkill.GetSkillID())
@@ -1643,11 +1922,14 @@
    Sync_PropertyRefreshView(turnFight, gameObj, ChConfig.AttrID_XP, updXP, addXP, diffType=1, relatedSkillID=relatedSkillID)
    return
def GetEnhanceXP(gameObj, addXP):
def GetEnhanceXP(gameObj, addXP, useSkill=None):
    ## 获取提升后的xp值
    addPer = gameObj.GetBatAttrValue(ChConfig.AttrID_XPRecoverPer)
    # 其他强化、弱化
    if addPer == 0:
        return addXP
    if useSkill and useSkill.GetFuncType() == ChConfig.Def_SkillFuncType_PotentialSkill and useSkill.GetSkillValue() == 50:
        GameWorld.DebugLogEx("潜能初始50点暂时写死不受限: objID=%s,addXP=%s,skillID=%s", gameObj.GetID(), addXP, useSkill.GetSkillID())
        return addXP
    objID = gameObj.GetID()
    updAddXP = int(addXP * max(10000 + addPer, 0) / 10000.0)
@@ -1707,6 +1989,10 @@
            GameWorld.DebugLogEx("有击杀目标时该技能无效! enhanceSkillID=%s", enhanceSkillID)
            return
        
    if checkHeroSex:
        if TurnPassive.GetTriggerEffectValue(turnFight, curBatObj, None, ChConfig.PassiveEff_IgnoreSex, connSkillTypeID=enhanceSkillData.GetSkillTypeID(), connSkillID=enhanceSkillID):
            checkHeroSex = 0
    # 继承主技能目标
    if enhanceSkillData.GetTagAim() == ChConfig.SkillTagAim_MainSkill:
        GameWorld.DebugLogEx("继承主技能目标! enhanceSkillID=%s", enhanceSkillID)
@@ -1718,8 +2004,11 @@
        for tagObj in tagObjList:
            tagID = tagObj.GetID()
            if not tagObj.IsAlive():
                GameWorld.DebugLogEx("    已被击杀不触发: tagID=%s", tagID)
                continue
                if enhanceSkillData.GetCurBuffState() == ChConfig.BatObjState_RebornLimit:
                    GameWorld.DebugLogEx("    禁止复活buff允许对死亡对象释放: tagID=%s", tagID)
                else:
                    GameWorld.DebugLogEx("    已被击杀不触发: tagID=%s", tagID)
                    continue
            if tagID in effIgnoreObjIDList:
                GameWorld.DebugLogEx("    闪避或免疫的不触发: tagID=%s", tagID)
                continue
@@ -1770,7 +2059,7 @@
    OnUseSkill(turnFight, curBatObj, enhanceSkillData, batType=ChConfig.TurnBattleType_Enhance, bySkill=useSkill)
    return
def OnUsePassiveSkill(turnFight, batObj, tagObj, passiveSkill, connSkill=None, effSkillID=0, effectID=0, connBuff=None, **kwargs):
def OnUsePassiveSkill(turnFight, batObj, tagObj, passiveSkill, connSkill=None, effSkillID=0, effectID=0, connBuff=None, batType=ChConfig.TurnBattleType_Passive, **kwargs):
    '''被动触发使用技能
    @param passiveSkill: 释放的被动技能 或 技能ID
    @param connSkill: 由什么技能引起的
@@ -1778,7 +2067,7 @@
    @param effectID: 被动效果ID
    注:可能由A引起触发B技能的效果释放技能C
    '''
    if not passiveSkill:
    if not passiveSkill or not batObj:
        return
    if isinstance(passiveSkill, int):
        passiveSkillID = passiveSkill
@@ -1820,24 +2109,46 @@
            GameWorld.DebugLogEx("    概率不触发: tagID=%s,happenRate=%s", tagID, happenRate)
            return
        passiveTagObjList = [tagObj]
        isOK = OnUseSkill(turnFight, batObj, passiveSkill, passiveTagObjList, batType=ChConfig.TurnBattleType_Passive, bySkill=connSkill, byBuff=connBuff, **kwargs)
        isOK = OnUseSkill(turnFight, batObj, passiveSkill, passiveTagObjList, batType=batType, bySkill=connSkill, byBuff=connBuff, **kwargs)
    elif tagAim == ChConfig.SkillTagAim_MainSkillEx:
        if not connSkill:
            return
        passiveTagObjList = connSkill.GetTagObjList()
        isOK = OnUseSkill(turnFight, batObj, passiveSkill, passiveTagObjList, batType=ChConfig.TurnBattleType_Passive, bySkill=connSkill, byBuff=connBuff, **kwargs)
        isOK = OnUseSkill(turnFight, batObj, passiveSkill, passiveTagObjList, batType=batType, bySkill=connSkill, byBuff=connBuff, **kwargs)
    elif tagAim == ChConfig.SkillTagAim_MainSkillFriend:
        if "byFriendObj" not in kwargs:
            return
        byFriendObj = kwargs.pop("byFriendObj") # 触发即移除,防止一直传递引发其他问题,如触发的技能再触发其他技能会被视为也是有byFriendObj
        passiveTagObjList = [byFriendObj]
        GameWorld.DebugLogEx("被动触发技能,针对来源友军! effSkillID=%s,effectID=%s,passiveSkillID=%s,bySkillID=%s,byFriendID=%s",
                             effSkillID, effectID, passiveSkillID, bySkillID, byFriendObj.GetID())
        isOK = OnUseSkill(turnFight, batObj, passiveSkill, passiveTagObjList, batType=batType, bySkill=connSkill, byBuff=connBuff, **kwargs)
    # 继承主技能目标同横排 11
    elif tagAim == ChConfig.SkillTagAim_MainSkillRow:
        if not tagObj:
            return
        GameWorld.DebugLogEx("被动触发技能,针对目标同排对象! effSkillID=%s,effectID=%s,passiveSkillID=%s,bySkillID=%s", effSkillID, effectID, passiveSkillID, bySkillID)
        passiveTagObjList = __getSameRowObjList(tagObj)
        isOK = OnUseSkill(turnFight, batObj, passiveSkill, passiveTagObjList, batType=batType, bySkill=connSkill, byBuff=connBuff, **kwargs)
    # 继承主技能目标同纵排 12
    elif tagAim == ChConfig.SkillTagAim_MainSkillCol:
        if not tagObj:
            return
        GameWorld.DebugLogEx("被动触发技能,针对目标同纵对象! effSkillID=%s,effectID=%s,passiveSkillID=%s,bySkillID=%s", effSkillID, effectID, passiveSkillID, bySkillID)
        passiveTagObjList = __getSameColObjList(tagObj)
        isOK = OnUseSkill(turnFight, batObj, passiveSkill, passiveTagObjList, batType=batType, bySkill=connSkill, byBuff=connBuff, **kwargs)
    else:
        GameWorld.DebugLogEx("被动触发技能,重新锁定目标! effSkillID=%s,effectID=%s,passiveSkillID=%s,bySkillID=%s", effSkillID, effectID, passiveSkillID, bySkillID)
        isOK = OnUseSkill(turnFight, batObj, passiveSkill, batType=ChConfig.TurnBattleType_Passive, bySkill=connSkill, byBuff=connBuff, **kwargs)
        isOK = OnUseSkill(turnFight, batObj, passiveSkill, batType=batType, bySkill=connSkill, byBuff=connBuff, **kwargs)
        
    return isOK
def CalcFormatHurt(turnFight, atkObj, defObj, curSkill, atkSkillValue, atkSkillPer, **kwargs):
def CalcFormatHurt(turnFight, atkObj, defObj, curSkill, atkSkillValue, atkSkillPer, addBatDamPer=0, **kwargs):
    '''按公式计算伤害,默认按攻击计算
    '''
    
    #mapID = turnFight.mapID
    skillID = curSkill.GetSkillID()
    #mapID = turnFight.mapID
    pmType = GetPMType(atkObj, curSkill)
    ignoreDef = IsIgnoreDef(curSkill)
    batType = curSkill.GetBatType()
@@ -1857,26 +2168,31 @@
    isAngerSkill = SkillCommon.isAngerSkill(curSkill)
    isDot = ("damageoftime" in kwargs)
    
    mustHit = False # 是否必命中
    if not isTurnNormalSkill: # 仅普攻判断是否闪避
        mustHit = True
    angerOverflow = atkObj.GetAngerOverflow() # 怒气溢出值
    if isAngerSkill:
        curXP = atkObj.GetXP()
        GameWorld.DebugLogEx("怒技攻击! curXP=%s,angerOverflow=%s", curXP, angerOverflow)
    #命中公式 攻击方类型不同,公式不同
    if not mustHit and not curSkill.GetEffectByID(ChConfig.SkillEff_MustHit) and not defObj.IsInControlledHard():
    # 常规闪避 - 仅针对普攻
    if isTurnNormalSkill and not curSkill.GetEffectByID(ChConfig.SkillEff_MustHit) and not defObj.IsInControlledHard():
        aMissRateDef = atkObj.GetBatAttrValue(ChConfig.AttrID_MissRateDef) #atkObj.GetHit() # 抗闪避率 - 命中
        dMissRate = defObj.GetBatAttrValue(ChConfig.AttrID_MissRate) # 闪避率
        missNum = curSkill.GetTagMissNum(defID)
        missRate = eval(IpyGameDataPY.GetFuncCompileCfg("MissCfg", 1))
        if GameWorld.CanHappen(missRate):
            GameWorld.DebugLogEx("闪避了! missRate=%s,dMissRate=%s,aMissRateDef=%s,missNum=%s", missRate, dMissRate, aMissRateDef, missNum)
            curSkill.SetTagMissNum(defID, missRate + 1)
            curSkill.SetTagMissNum(defID, missNum + 1)
            return 0, pow(2, ChConfig.HurtAtkType_Miss)
        
    # 绝对闪避 - 除dot外的所有技能,因为绝对闪避不触发常规闪避的被动,所以在常规闪避后判断
    if not isDot:
        aMissRateDef = atkObj.GetBatAttrValue(ChConfig.AttrID_AbsoluteMissRateDef)
        dMissRate = defObj.GetBatAttrValue(ChConfig.AttrID_AbsoluteMissRate)
        missNum = curSkill.GetTagMissNum(defID)
        if dMissRate > 0:
            missRate = eval(IpyGameDataPY.GetFuncCompileCfg("MissCfg", 1))
            if GameWorld.CanHappen(missRate):
                GameWorld.DebugLogEx("绝对闪避了! missRate=%s,dAbsoluteMissRate=%s,aAbsoluteMissRateDef=%s,missNum=%s", missRate, dMissRate, aMissRateDef, missNum)
                curSkill.SetTagMissNum(defID, missNum + 1)
                return 0, pow(2, ChConfig.HurtAtkType_AbsoluteMiss)
    angerOverflow = atkObj.GetAngerOverflow() # 怒气溢出值
    hurtTypes = pow(2, ChConfig.HurtAtkType_Hurt)
    
    isSuperHit, isParry, isStun = False, False, False
@@ -1897,7 +2213,6 @@
        aSuperDamPer += TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.AttrID_SuperDamPer, curSkill)
        
        dSuperDamPerDef = defObj.GetBatAttrValue(ChConfig.AttrID_SuperDamPerDef)
        GameWorld.DebugLogEx("aSuperDamPer=%s,dSuperDamPerDef=%s", aSuperDamPer, dSuperDamPerDef)
        
    if isParry:
        hurtTypes |= pow(2, ChConfig.HurtAtkType_Parry)
@@ -1925,6 +2240,32 @@
    aFinalDamPer += TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.PassiveEff_AddFinalDamPer, curSkill)
    aFinalDamPer += TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.AttrID_FinalDamPer, curSkill)
    
    #招架  - 无视攻击方最终增伤百分比
    dZhaojia = defObj.GetBatAttrValue(ChConfig.AttrID_Zhaojia)
    aZhaojiaDef = atkObj.GetBatAttrValue(ChConfig.AttrID_ZhaojiaDef)
    if dZhaojia > aZhaojiaDef:
        perMoreValue, perReduce = IpyGameDataPY.GetFuncEvalCfg("ZhaojiaCfg", 1) # 每高与对方抗贯穿x值无视y%最终减伤
        reducePer = (dZhaojia - aZhaojiaDef) / float(perMoreValue) * perReduce
        reduceMin, reduceMax = IpyGameDataPY.GetFuncEvalCfg("ZhaojiaCfg", 2) # 最小无视百分比|最大无视百分比
        if reducePer >= reduceMin and GameWorld.CanHappen(IpyGameDataPY.GetFuncCfg("ZhaojiaCfg", 3)):
            reducePer = min(reducePer, reduceMax)
            hurtTypes |= pow(2, ChConfig.HurtAtkType_Zhaojia)
            aFinalDamPer = int(aFinalDamPer * (100 - reducePer) / 100.0) # 按百分比减少
            GameWorld.DebugLogEx("dZhaojia=%s,aZhaojiaDef=%s,reducePer=%s,aFinalDamPer=%s", dZhaojia, aZhaojiaDef, reducePer, aFinalDamPer)
    #贯穿  - 无视防守方最终减伤百分比
    aGuanchuan = atkObj.GetBatAttrValue(ChConfig.AttrID_Guanchuan)
    dGuanchuanDef = defObj.GetBatAttrValue(ChConfig.AttrID_GuanchuanDef)
    if aGuanchuan > dGuanchuanDef:
        perMoreValue, perReduce = IpyGameDataPY.GetFuncEvalCfg("GuanchuanCfg", 1) # 每高与对方抗贯穿x值无视y%最终减伤
        reducePer = (aGuanchuan - dGuanchuanDef) / float(perMoreValue) * perReduce
        reduceMin, reduceMax = IpyGameDataPY.GetFuncEvalCfg("GuanchuanCfg", 2) # 最小无视百分比|最大无视百分比
        if reducePer >= reduceMin and GameWorld.CanHappen(IpyGameDataPY.GetFuncCfg("GuanchuanCfg", 3)):
            reducePer = min(reducePer, reduceMax)
            hurtTypes |= pow(2, ChConfig.HurtAtkType_Guanchuan)
            dFinalDamPerDef = int(dFinalDamPerDef * (100 - reducePer) / 100.0) # 按百分比减少
            GameWorld.DebugLogEx("aGuanchuan=%s,dGuanchuanDef=%s,reducePer=%s,dFinalDamPerDef=%s", aGuanchuan, dGuanchuanDef, reducePer, dFinalDamPerDef)
    aNormalSkillPer, dNormalSkillPerDef = 0, 0
    if isTurnNormalSkill:
        aNormalSkillPer = atkObj.GetBatAttrValue(ChConfig.AttrID_NormalSkillPer) # 普技增伤
@@ -1942,11 +2283,17 @@
        aDOTPer = atkObj.GetBatAttrValue(ChConfig.AttrID_DOTPer)
        dDOTPerDef = defObj.GetBatAttrValue(ChConfig.AttrID_DOTPerDef)
        GameWorld.DebugLogEx("aDOTPer=%s,dDOTPerDef=%s", aDOTPer, dDOTPerDef)
        if curSkill.GetCurBuffState() in ChConfig.BurnStateList:
            aDOTPer += atkObj.GetBatAttrValue(ChConfig.AttrID_DOTBurnPer)
            dDOTPerDef += defObj.GetBatAttrValue(ChConfig.AttrID_DOTBurnPerDef)
            GameWorld.DebugLogEx("是灼烧: aDOTPer=%s,dDOTPerDef=%s", aDOTPer, dDOTPerDef)
    #aAddSkillPer = 0 # 技能增伤
    aBatDamPer, dBatDamPerDef = 0, 0 # 战斗增减伤
    dBatDamPerDef = 0 # 战斗增减伤
    aBatDamPer = atkObj.GetBatAttrValue(ChConfig.AttrID_BatDamPer)
    aBatDamPer += TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.AttrID_BatDamPer, curSkill)
    aBatDamPer += TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.PassiveEff_AddBatDamPerByTagLostHP, curSkill)
    aBatDamPer += addBatDamPer
    
    # 物法增减伤
    if pmType == IPY_GameWorld.ghtMag: # 法伤
@@ -1981,7 +2328,6 @@
    if turnFight.isPVP() > 0:
        aPVPDamPer = atkObj.GetBatAttrValue(ChConfig.AttrID_PVPDamPer)
        dPVPDamPerDef = defObj.GetBatAttrValue(ChConfig.AttrID_PVPDamPerDef)
        GameWorld.DebugLogEx("PVP aPVPDamPer=%s,dPVPDamPerDef=%s", aPVPDamPer, dPVPDamPerDef)
        
    # 所有万分率参数统一除10000.0
    atkSkillPer /= 10000.0
@@ -2010,19 +2356,30 @@
    
    if calcType != ChConfig.Def_Calc_Attack:
        aAtk = GetCalcBaseValue(calcType, atkObj, defObj, curSkill)
    GameWorld.DebugLogEx("伤血计算: atkID=%s,defID=%s,skillID=%s,atkSkillPer=%s,calcType=%s,aAtk=%s,dDef=%s,dHP=%s/%s,hurtTypes=%s,aBatDamPer=%s,aFinalDamPer=%s",
                         atkID, defID, skillID, atkSkillPer, calcType, aAtk, dDef, dHP, dMaxHP, hurtTypes, aBatDamPer, aFinalDamPer)
    #GameWorld.DebugLogEx("aCountry=%s,dCountry=%s,aCountryDamPer=%s,dCountryDamPerDef=%s", aCountry, dCountry, aCountryDamPer, dCountryDamPerDef)
    GameWorld.DebugLogEx("伤血计算: atkID=%s,defID=%s,skillID=%s,atkSkillPer=%s,calcType=%s,aAtk=%s,dDef=%s,dHP=%s/%s,hurtTypes=%s,aBatDamPer=%s,dBatDamPerDef=%s,aFinalDamPer=%s,dFinalDamPerDef=%s",
                         atkID, defID, skillID, atkSkillPer, calcType, aAtk, dDef, dHP, dMaxHP, hurtTypes, aBatDamPer, dBatDamPerDef, aFinalDamPer, dFinalDamPerDef)
    if isSuperHit:
        GameWorld.DebugLogEx("    暴击 aSuperDamPer=%s,dSuperDamPerDef=%s", aSuperDamPer, dSuperDamPerDef)
    if aPMDamPer or dPMDamPerDef:
        GameWorld.DebugLogEx("    物法 aPMDamPer=%s,dPMDamPerDef=%s,pmType=%s", aPMDamPer, dPMDamPerDef, pmType)
    if aComboDamPer or dComboDamPerDef:
        GameWorld.DebugLogEx("    连击 aComboDamPer=%s,dComboDamPerDef=%s", aComboDamPer, dComboDamPerDef)
    if aPursueDamPer or dPursueDamPerDef:
        GameWorld.DebugLogEx("    追击 aPursueDamPer=%s,dPursueDamPerDef=%s", aPursueDamPer, dPursueDamPerDef)
    if aPVPDamPer or dPVPDamPerDef:
        GameWorld.DebugLogEx("    PVP aPVPDamPer=%s,dPVPDamPerDef=%s", aPVPDamPer, dPVPDamPerDef)
    if aCountryDamPer or dCountryDamPerDef:
        GameWorld.DebugLogEx("    国家 aCountry=%s,dCountry=%s,aCountryDamPer=%s,dCountryDamPerDef=%s", aCountry, dCountry, aCountryDamPer, dCountryDamPerDef)
    if isTurnNormalSkill:
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 1))
        GameWorld.DebugLogEx("    普攻技能伤害=%s,aNormalSkillPer=%s,dNormalSkillPerDef=%s", hurtValue, aNormalSkillPer, dNormalSkillPerDef)
    elif isAngerSkill:
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 2))
        GameWorld.DebugLogEx("    怒气技能伤害=%s,aAngerSkillPer=%s,dAngerSkillPerDef=%s", hurtValue, aAngerSkillPer, dAngerSkillPerDef)
        GameWorld.DebugLogEx("    怒气技能伤害=%s,aAngerSkillPer=%s,dAngerSkillPerDef=%s,angerOverflow=%s,curXP=%s", hurtValue, aAngerSkillPer, dAngerSkillPerDef, angerOverflow, atkObj.GetXP())
    elif isDot:
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("DOTFormula", 1))
        GameWorld.DebugLogEx("    持续技能伤害=%s" % (hurtValue))
        GameWorld.DebugLogEx("    持续技能伤害=%s,aDOTPer=%s,dDOTPerDef=%s", hurtValue, aDOTPer, dDOTPerDef)
    elif calcType == ChConfig.Def_Calc_ByBuffValue:
        hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("CalcTypeFormula", 2))
        GameWorld.DebugLogEx("    按最后一次buff值计算伤害=%s,calcType=%s,LastHurt=%s", hurtValue, calcType, aAtk)
@@ -2045,20 +2402,32 @@
        
    hurtAtkPerMax = curSkill.GetHurtAtkPerMax() # 最大万分比,限制最终伤害不超过攻击力万分率
    if hurtAtkPerMax:
        hurtAtkPerMax += TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.PassiveEff_AddHurtAtkPerMax, curSkill)
        aAtk = atkObj.GetAtk()
        hurtValueMax = aAtk * hurtAtkPerMax / 10000.0
        hurtValue = min(hurtValue, hurtValueMax)
        GameWorld.DebugLogEx("    伤害最高限制: hurtValue=%s,hurtAtkPerMax=%s,aAtk=%s", hurtValue, hurtAtkPerMax, aAtk)
        
    # 均摊
    hurtShareEff = curSkill.GetEffectByID(ChConfig.SkillEff_HurtShare)
    if hurtShareEff:
    if HaveShareEff(atkObj, curSkill):
        tagCnt = max(1, len(curSkill.GetTagObjList()))
        hurtValue = hurtValue / tagCnt
        GameWorld.DebugLogEx("    目标均摊伤害: hurtValue=%s,tagCnt=%s", hurtValue, tagCnt)
        
    hurtValue = max(1, int(hurtValue)) # 负值、保底防范,放最后
    return hurtValue, hurtTypes
def HaveShareEff(atkObj, curSkill):
    ## 玩家技能是否有分摊效果: 均摊伤害/治疗/承伤盾值
    hurtShareEff = curSkill.GetEffectByID(ChConfig.SkillEff_HurtShare)
    if not hurtShareEff:
        return False
    needLearnSkillID = hurtShareEff.GetEffectValue(0)
    if needLearnSkillID:
        if not atkObj.GetSkillManager().FindSkillByID(needLearnSkillID):
            GameWorld.DebugLogEx("所需技能未学习,分摊效果不生效! skillID=%s,needLearnSkillID=%s", curSkill.GetSkillID(), needLearnSkillID)
            return False
    return True
def GetAddSkillPer(turnFight, atkObj, defObj, curSkill):
    ## 获取额外增加的技能万分比
@@ -2073,7 +2442,6 @@
def CanSuperHit(turnFight, atkObj, defObj, curSkill):
    if TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.PassiveEff_MustSuperHit, curSkill):
        GameWorld.DebugLogEx("目标血量低于百分x时必定暴击: defID=%s,hp:%s/%s", defObj.GetID(), defObj.GetHP(), defObj.GetMaxHP())
        return True
    
    aSuperHitRate = atkObj.GetBatAttrValue(ChConfig.AttrID_SuperHitRate)
@@ -2134,7 +2502,7 @@
        return hurtValue, hurtTypes, immuneHurt
    
    hurtValue = int(hurtValue)
    if defObj.CheckInState(ChConfig.BatObjState_Wudi):
    if defObj.CheckInState(ChConfig.ImmuneHurtStateList):
        hurtTypes |= pow(2, ChConfig.HurtAtkType_Immune) # 添加免疫
        immuneHurt = hurtValue
        hurtValue = 0
@@ -2164,7 +2532,7 @@
        defObj, hurtValue, hurtTypes, immuneHurt = hurtInfo[:4]
        isEx = hurtInfo[4] if len(hurtInfo) > 4 else 0 # 是否是额外目标
        
        lostHP, ignoreShield = DoLostHP(turnFight, atkObj, defObj, hurtValue, curSkill, lostType, hpCanNegative=True, immuneHurt=immuneHurt)
        lostHP, ignoreShield, hurtValue = DoLostHP(turnFight, atkObj, defObj, hurtValue, curSkill, lostType, hpCanNegative=True, immuneHurt=immuneHurt)
        if ignoreShield:
            hurtTypes |= pow(2, ChConfig.HurtAtkType_IgnoreShield)
            
@@ -2305,7 +2673,7 @@
    # 曹仁的分担触发条件暂时程序内固定,有修改时程序同步修改
    # 曹仁: 我方英雄受到直接攻击时(受到群攻时选择血量最低的友方进行防护),曹仁有30%概率为其分担一半所受伤害,每成功分担一次,曹仁获得1层坚韧,使我方全体减伤提升5%,最多6层,曹仁死亡效果消失
    # 董白怒气    同命锁    对敌方攻击最高的1个目标造成攻击力450%物理伤害,并与其连接,使两人受到的任意伤害平摊(平摊伤害不会被再次平摊),持续2回合
    caorenSkillID = 1015070
    caorenSkillID = ChConfig.SkillID_CaorenProtect
    effHeroIDList = [ChConfig.HeroID_Zhenfu, ChConfig.HeroID_Caoren, ChConfig.HeroID_Dongbai] # 暂固定,有增加武将再同步修改
    
    isAve = False
@@ -2318,12 +2686,10 @@
                         faction, lineupNum, hurtValueDict, immuneHurtDict)
    # 按优先级顺序处理拥有分摊效果的武将
    for effHeroID in effHeroIDList:
        if effHeroID not in batLineup.heroObjIDDict:
            continue
        objID = batLineup.heroObjIDDict[effHeroID]
        batObj = batObjMgr.getBatObj(objID)
        batObj = batLineup.getHeroObj(effHeroID)
        if not batObj or not batObj.IsAlive():
            continue
        objID = batObj.GetID()
        buffMgr = batObj.GetBuffManager()
        
        inHurt = objID in hurtValueDict # 光环里的人员是否有受伤
@@ -2418,7 +2784,7 @@
            
            immuneHurt = immuneHurtDict.get(aveObjID)
            if immuneHurt == None:
                if aveObj.CheckInState(ChConfig.BatObjState_Wudi):
                if aveObj.CheckInState(ChConfig.ImmuneHurtStateList):
                    immuneHurt = 0
                else:
                    immuneHurt = -1 # 标记没有无敌
@@ -2454,7 +2820,7 @@
    @param hpCanNegative: 扣除后的生命是否允许负值
    @param immuneHurt: 免疫的伤害值
    @param isSkillSelfTag: 是否技能自身的直接目标,如平摊伤害目标、溅射伤害目标这种就不算直接目标
    @return: lostHP, ignoreShield
    @return: lostHP, ignoreShield, hurtValue
    '''
    
    ignoreShield = None
@@ -2472,8 +2838,14 @@
                                 defObj.GetID(), buff.GetBuffID(), buffSkillID, buffValue, immuneHurt, updBuffValue, lostType)
            
    if hurtValue <= 0:
        return 0, ignoreShield
        return 0, ignoreShield, hurtValue
    
    # 伤血上限保护
    lostHPProtect = TurnPassive.GetTriggerEffectValue(turnFight, defObj, atkObj, ChConfig.PassiveEff_LostHPProtect, curSkill)
    if lostHPProtect > 0 and hurtValue > lostHPProtect:
        GameWorld.DebugLogEx("    扣血时最大伤血保护: defID=%s,hurtValue=%s,lostHPProtect=%s", defObj.GetID(), hurtValue, lostHPProtect)
        hurtValue = lostHPProtect
    atkID = atkObj.GetID()
    defID = defObj.GetID()
    skillID = curSkill.GetSkillID()
@@ -2539,7 +2911,7 @@
    GameWorld.DebugLogEx("    扣血: 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, ignoreShield
    return lostHP, ignoreShield, hurtValue
def CalcBounceHP(turnFight, atkObj, defObj, hurtObj, curSkill):
    '''计算反弹反弹伤害
@@ -2551,7 +2923,9 @@
    damBackPer = 0
    buffMgr = defObj.GetBuffManager()
    for buff in buffMgr.FindBuffListByState(ChConfig.BatObjState_DamBackShield):
        damBackPer += buff.GetValue1()
        damBackPer += buff.GetValue1() * max(1, buff.GetLayer())
        connSkillTypeID = buff.GetSkillTypeID()
        damBackPer += TurnPassive.GetTriggerEffectValue(turnFight, defObj, atkObj, ChConfig.PassiveEff_AddDamBackPerByBuffLayer, connSkillTypeID=connSkillTypeID)
        
    if damBackPer <= 0:
        return
@@ -2561,12 +2935,12 @@
        return
    
    GameWorld.DebugLogEx("    反弹伤害=%s,%s/%s, damBackPer=%s", bounceHP, atkObj.GetHP(), atkObj.GetMaxHP(), damBackPer)
    bounceHP, _, immuneHurt = CalcHurtWithBuff(turnFight, atkObj, defObj, bounceHP)
    bounceHP, _, immuneHurt = CalcHurtWithBuff(turnFight, defObj, atkObj, bounceHP)
    if bounceHP <= 0:
        GameWorld.DebugLogEx("        bounceHP=%s", bounceHP)
        return
    hurtObj.SetBounceHP(bounceHP)
    DoLostHP(turnFight, atkObj, defObj, bounceHP, curSkill, "Bounce", hpCanNegative=True, immuneHurt=immuneHurt) # 反弹后生命允许负值
    DoLostHP(turnFight, defObj, atkObj, bounceHP, curSkill, "Bounce", hpCanNegative=True, immuneHurt=immuneHurt) # 反弹后生命允许负值
    return
def CalcSuckBlood(turnFight, atkObj, defObj, hurtObj, curSkill):
@@ -2664,12 +3038,14 @@
        cureHP += cureHPEx
        GameWorld.DebugLogEx("    额外治疗值(%s): cureType=%s,baseValue=%s,skillPer=%s,cureHP=%s", cureHPEx, cureType, baseValue, skillPer, cureHP)
        
    addCureMulti = TurnPassive.GetTriggerEffectValue(turnFight, userObj, tagObj, ChConfig.PassiveEff_AddCureMultiBySkillType, curSkill)
    if addCureMulti:
        multiValue += addCureMulti / 100.0
    if multiValue and multiValue != 1:
        cureHP = int(cureHP * multiValue)
        GameWorld.DebugLogEx("    治疗倍值: cureHP=%s,multiValue=%s", cureHP, multiValue)
        
    hurtShareEff = curSkill.GetEffectByID(ChConfig.SkillEff_HurtShare)
    if hurtShareEff:
    if HaveShareEff(userObj, curSkill):
        tagCnt = max(1, len(curSkill.GetTagObjList()))
        cureHP = cureHP / tagCnt
        GameWorld.DebugLogEx("    目标均摊治疗: cureHP=%s,tagCnt=%s", cureHP, tagCnt)
@@ -2771,6 +3147,43 @@
    useSkill.ResetUseRec()
    return
def DoHurtExAtk(turnFight, atkObj, defObj, hurtValue, skillID, hurtTypes=0):
    ## 额外结算指定伤害
    skillIpyData = IpyGameDataPY.GetIpyGameData("Skill", skillID)
    if not skillIpyData:
        return
    atkID = atkObj.GetID()
    defID = defObj.GetID()
    # 结算需要同步标签
    useTag = "Skill_%s_%s_Dot_%s" % (atkID, skillID, atkObj.GetIncrementValue()) # 复用dot的标签
    Sync_TurnFightTag(turnFight, useTag, 0)
    dHP = defObj.GetHP()
    dMaxHP = defObj.GetMaxHP()
    GameWorld.DebugLogEx("额外结算指定伤害: atkID=%s,defID=%s,skillID=%s,hurtValue=%s,,dHP=%s/%s",
                         atkID, defID, skillID, hurtValue, dHP, dMaxHP)
    useSkill = BattleObj.PySkill(skillIpyData, atkID)
    useSkill.SetTagObjList([defObj])
    useSkill.SetBatType(ChConfig.TurnBattleType_Dot) # 要修改表现的话等前端同步修改
    hurtTypes |= pow(2, ChConfig.HurtAtkType_Hurt) # 确保有基础的伤害类型
    hurtValue, hurtTypes, immuneHurt = CalcHurtWithBuff(turnFight, atkObj, defObj, hurtValue, hurtTypes)
    calcHurtResults = [[defObj, hurtValue, hurtTypes, immuneHurt]]
    DoSkillHurtHP(turnFight, atkObj, useSkill, calcHurtResults, "HurtExAtk")
    Sync_UseSkill(turnFight, atkObj, useSkill)
    DoBeAttackResult(turnFight, atkObj, useSkill)
    # 通知结束标签
    Sync_TurnFightTag(turnFight, useTag, 1)
    useSkill.ResetUseRec()
    return True
def Sync_TurnFightTag(turnFight, useTag, sign):
    ## 通知技能标签
    clientPack = ChPyNetSendPack.tagSCTurnFightTag()