| | |
| | | # 可扩展其他目标选择,如复活技能没有死亡单位时则使用另外的效果
|
| | | return
|
| | |
|
| | | objID = curBatObj.GetID()
|
| | | oneActionUseCnt = turnFight.GetOneActionUseSkillCnt(objID)
|
| | | if oneActionUseCnt >= 20:
|
| | | GameWorld.ErrLog("单次行动累计使用技能达到上限! objID=%s,oneActionUseCnt=%s" % (objID, oneActionUseCnt), turnFight.playerID)
|
| | | return
|
| | | oneActionUseCnt = turnFight.SetOneActionUseSkillCnt(objID, oneActionUseCnt + 1)
|
| | | |
| | | bySkillID = bySkill.GetSkillID() if bySkill else 0
|
| | | GameWorld.DebugLog("使用技能: curID=%s,skillID=%s,tagCnt=%s,batType=%s,bySkillID=%s" |
| | | % (curBatObj.GetID(), skillID, len(tagObjList), batType, bySkillID))
|
| | | GameWorld.DebugLog("●使用技能: curID=%s,skillID=%s,tagCnt=%s,batType=%s,bySkillID=%s,HP:%s/%s,oneActionUseCnt=%s" |
| | | % (objID, skillID, len(tagObjList), batType, bySkillID, curBatObj.GetHP(), curBatObj.GetMaxHP(), oneActionUseCnt))
|
| | | # 以下为技能可以使用的处理,之后的逻辑默认技能使用成功
|
| | |
|
| | | poolMgr = ObjPool.GetPoolMgr()
|
| | |
| | | if isinstance(useSkill, IpyGameDataPY.IPY_Skill):
|
| | | usePoolSkill = True
|
| | | # 统一使用 BattleObj.PySkill
|
| | | useSkill = poolMgr.acquire(BattleObj.PySkill, useSkill)
|
| | | useSkill = poolMgr.acquire(BattleObj.PySkill, useSkill, objID)
|
| | |
|
| | | useSkill.ResetUseRec()
|
| | | useSkill.SetTagObjList(tagObjList)
|
| | | useSkill.SetBatType(batType)
|
| | | useSkill.SetBySkill(bySkill)
|
| | |
|
| | | isTurnNormalSkill = SkillCommon.isTurnNormalSkill(useSkill)
|
| | | if isTurnNormalSkill:
|
| | | # 普攻追击时强制重新开始算连击
|
| | | useSkill.ComboCheckStart(batType == ChConfig.TurnBattleType_Pursue)
|
| | | |
| | | curBatObj.ClearSkillTempAttr()
|
| | | tagIDList = []
|
| | | for tagObj in tagObjList:
|
| | | tagIDList.append(tagObj.GetID())
|
| | | tagObj.ClearSkillTempAttr()
|
| | |
|
| | | objID = curBatObj.GetID()
|
| | | # 有功能分类的技能都认为是主技能
|
| | | if useSkill.GetFuncType():
|
| | | curBatObj.SetMainTagIDList(tagIDList)
|
| | | |
| | | useTag = ""
|
| | |
|
| | | #这个技能是Buff
|
| | |
| | | turnFight.addBatPack(clientPack)
|
| | |
|
| | | # 处理反击 或 连击
|
| | | DoCombo(turnFight, curBatObj, useSkill)
|
| | | |
| | | if isTurnNormalSkill:
|
| | | if not DoCombo(turnFight, curBatObj, useSkill):
|
| | | useSkill.ComboInterrupt()
|
| | | |
| | | # 最后重置、回收对象
|
| | | useSkill.ResetUseRec()
|
| | | if usePoolSkill:
|
| | |
| | |
|
| | | skillID = useSkill.GetSkillID()
|
| | | for tagBatObj in useSkill.GetTagObjList():
|
| | | cureHP = CalcCureHP(curBatObj, tagBatObj, useSkill, largeNum=True)
|
| | | cureHP = CalcCureHP(turnFight, curBatObj, tagBatObj, useSkill, largeNum=True)
|
| | | if cureHP <= 0:
|
| | | continue
|
| | |
|
| | |
| | | DoBeAttackResult(turnFight, curBatObj, useSkill, True)
|
| | | return
|
| | |
|
| | | def DoCombo(turnFight, curBatObj, useSkill):
|
| | | def DoCombo(turnFight, atkObj, useSkill):
|
| | | '''
|
| | | 格挡、反击、连击规则
|
| | | 1. 所有武将或怪物均可能产生格挡,群攻时格挡一对一判断,均可能产生格挡
|
| | |
| | | 纵排: 优先前面的单位
|
| | | '''
|
| | |
|
| | | if not SkillCommon.isTurnNormalSkill(useSkill):
|
| | | #GameWorld.DebugLog("非普攻不处理反击连击")
|
| | | if not useSkill.ComboEnable():
|
| | | return
|
| | |
|
| | | tagFriendly = useSkill.GetTagFriendly()
|
| | | if tagFriendly:
|
| | | tagObj = GetRelativeObj(turnFight, curBatObj)
|
| | | tagObj = GetRelativeObj(turnFight, atkObj)
|
| | | else:
|
| | | tagObjList = useSkill.GetTagObjList()
|
| | | if not tagObjList:
|
| | |
| | | if atkBackSkill:
|
| | | # 可以反击,打断连击
|
| | | GameWorld.DebugLog("● %s 【反击】" % TurnAttack.GetObjName(tagObj))
|
| | | OnUseSkill(turnFight, tagObj, atkBackSkill, [curBatObj], ChConfig.TurnBattleType_AtkBack)
|
| | | OnUseSkill(turnFight, tagObj, atkBackSkill, [atkObj], ChConfig.TurnBattleType_AtkBack)
|
| | | return
|
| | |
|
| | | if not tagObj:
|
| | | return
|
| | |
|
| | | if CanCombo(curBatObj, tagObj):
|
| | | # 连击根据技能目标配置逻辑重新选择目标
|
| | | GameWorld.DebugLog("● %s 【连击】" % TurnAttack.GetObjName(curBatObj))
|
| | | OnUseSkill(turnFight, curBatObj, useSkill, batType=ChConfig.TurnBattleType_Combo)
|
| | | |
| | | return
|
| | | comboNum = useSkill.GetComboNum()
|
| | | aComboRate = atkObj.GetBatAttrValue(ChConfig.AttrID_ComboRate)
|
| | | aComboRate += TurnPassive.GetTriggerEffectValue(turnFight, atkObj, tagObj, ChConfig.AttrID_ComboRate, useSkill)
|
| | | |
| | | dComboRateDef = tagObj.GetBatAttrValue(ChConfig.AttrID_ComboRateDef)
|
| | | happenRate = eval(IpyGameDataPY.GetFuncCompileCfg("ComboCfg", 1))
|
| | | if not GameWorld.CanHappen(happenRate):
|
| | | GameWorld.DebugLog("无法连击! atkID=%s,happenRate=%s,aComboRate=%s,dComboRateDef=%s,comboNum=%s" |
| | | % (atkObj.GetID(), happenRate, aComboRate, dComboRateDef, comboNum))
|
| | | return
|
| | | GameWorld.DebugLog("● %s 【连击】 happenRate=%s,aComboRate=%s,dComboRateDef=%s,comboNum=%s" |
| | | % (TurnAttack.GetObjName(atkObj), happenRate, aComboRate, dComboRateDef, comboNum))
|
| | | useSkill.SetComboNum(comboNum + 1)
|
| | | |
| | | # 连击特长
|
| | | DoHeroSpecialty(turnFight, atkObj, ChConfig.HeroSpecialty_Combo, useSkill.GetSkillID())
|
| | | |
| | | # 连击根据技能目标配置逻辑重新选择目标
|
| | | OnUseSkill(turnFight, atkObj, useSkill, batType=ChConfig.TurnBattleType_Combo)
|
| | | return True
|
| | |
|
| | | def __getCanAtkBackSkill(useSkill, tagObj):
|
| | | ## 获取是否可反击及反击技能
|
| | |
| | | return
|
| | |
|
| | | tagID = tagObj.GetID()
|
| | | if tagObj.GetAtkDistType() != ChConfig.AtkDistType_Short:
|
| | | GameWorld.DebugLog("非近战不可反击! tagID=%s" % tagID)
|
| | | canAtkbackDictTypeList = IpyGameDataPY.GetFuncEvalCfg("ParryCfg", 2)
|
| | | if tagObj.GetAtkDistType() not in canAtkbackDictTypeList:
|
| | | GameWorld.DebugLog("该远近类型武将不可反击! tagID=%s,AtkDistType=%s not in %s" % (tagID, tagObj.GetAtkDistType(), canAtkbackDictTypeList))
|
| | | return
|
| | |
|
| | | canAtkBack = False
|
| | |
| | | useSkill = skillManager.GetSkillByIndex(index)
|
| | | if not useSkill:
|
| | | continue
|
| | | if useSkill.GetFuncType() == ChConfig.Def_SkillFuncType_AtkbackSkill:
|
| | | if useSkill.GetFuncType() == ChConfig.Def_SkillFuncType_TurnNormaSkill: # 使用普攻反击
|
| | | GameWorld.DebugLog("可以反击! tagID=%s" % tagID)
|
| | | return useSkill
|
| | | return
|
| | |
|
| | | def CanCombo(atkObj, defObj):
|
| | | ## 可否连击
|
| | | comboNum = atkObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnComboNum)
|
| | | aComboRate = atkObj.GetBatAttrValue(ChConfig.AttrID_ComboRate)
|
| | | dComboRateDef = defObj.GetBatAttrValue(ChConfig.AttrID_ComboRateDef)
|
| | | happenRate = eval(IpyGameDataPY.GetFuncCompileCfg("ComboCfg", 1))
|
| | | if GameWorld.CanHappen(happenRate):
|
| | | GameWorld.DebugLog("可以连击! atkID=%s,happenRate=%s,aComboRate=%s,dComboRateDef=%s,comboNum=%s" |
| | | % (atkObj.GetID(), happenRate, aComboRate, dComboRateDef, comboNum))
|
| | | atkObj.SetDict(ChConfig.Def_Obj_Dict_TurnComboNum, comboNum + 1)
|
| | | return True
|
| | | GameWorld.DebugLog("无法连击! atkID=%s,happenRate=%s,aComboRate=%s,dComboRateDef=%s,comboNum=%s" |
| | | % (atkObj.GetID(), happenRate, aComboRate, dComboRateDef, comboNum))
|
| | | return False
|
| | |
|
| | | def DoBeAttackResult(turnFight, curObj, useSkill, isUseSkill=False):
|
| | | '''被攻击结果
|
| | | @param curObj: 施法方或buff归属方
|
| | | @param isUseSkill: 是否是直接使用技能的攻击结果
|
| | | @param isUseSkill: 是否是直接使用技能的攻击结果,否则视为持续性的
|
| | | '''
|
| | |
|
| | | curID = curObj.GetID()
|
| | |
| | | TurnAttack.SetObjKilled(turnFight, curObj)
|
| | |
|
| | | # 统计伤血,可能单个技能对同一目标造成多次伤害
|
| | | missObjIDList = []
|
| | | totalHurtValue = 0
|
| | | isSuperHit, isStun, isSuckHP = False, False, False
|
| | | missObjIDList, immuneObjIDList = [], []
|
| | | for hurtObj in useSkill.GetHurtObjList():
|
| | | hurtObjID = hurtObj.GetObjID()
|
| | | tagObj = batObjMgr.getBatObj(hurtObjID)
|
| | | if not tagObj:
|
| | | continue
|
| | | if not hurtObj.HaveHurtType(ChConfig.HurtTYpe_Recovery) and (isTurnNormalSkill or isAngerSkill) and tagObj.IsAlive():
|
| | | if not hurtObj.HaveHurtType(ChConfig.HurtTYpe_Recovery) and not hurtObj.HaveHurtType(ChConfig.HurtType_Immune) \
|
| | | and (isTurnNormalSkill or isAngerSkill) and tagObj.IsAlive():
|
| | | __doSkillHurtAnger(tagObj, hurtObj.GetLostHP(), useSkill)
|
| | | if hurtObj.HaveHurtType(ChConfig.HurtType_Miss):
|
| | | missObjIDList.append(hurtObjID)
|
| | |
|
| | | if hurtObj.HaveHurtType(ChConfig.HurtType_Normal):
|
| | | totalHurtValue += hurtObj.GetHurtHP()
|
| | | |
| | | if hurtObj.HaveHurtType(ChConfig.HurtType_Miss):
|
| | | if hurtObjID not in missObjIDList:
|
| | | missObjIDList.append(hurtObjID)
|
| | | DoHeroSpecialty(turnFight, tagObj, ChConfig.HeroSpecialty_Miss, relatedSkillID)
|
| | | |
| | | if hurtObj.HaveHurtType(ChConfig.HurtType_Immune):
|
| | | if hurtObjID not in immuneObjIDList:
|
| | | immuneObjIDList.append(hurtObjID)
|
| | | |
| | | if hurtObj.HaveHurtType(ChConfig.HurtType_Parry):
|
| | | DoHeroSpecialty(turnFight, tagObj, ChConfig.HeroSpecialty_Parry, relatedSkillID)
|
| | | |
| | | if hurtObj.HaveHurtType(ChConfig.HurtType_SuperHit):
|
| | | isSuperHit = True
|
| | | |
| | | if hurtObj.HaveHurtType(ChConfig.HurtType_Stun):
|
| | | isStun = True
|
| | | |
| | | if hurtObj.GetSuckHP() > 0:
|
| | | isSuckHP = True
|
| | | |
| | | curObj.SetLastHurtValue(totalHurtValue) # 记录最后一次总伤害
|
| | | |
| | | # 群攻只触发一次特长
|
| | | if isSuperHit:
|
| | | DoHeroSpecialty(turnFight, curObj, ChConfig.HeroSpecialty_SuperHit, relatedSkillID)
|
| | | if isStun:
|
| | | DoHeroSpecialty(turnFight, curObj, ChConfig.HeroSpecialty_Stun, relatedSkillID)
|
| | | if isSuckHP:
|
| | | DoHeroSpecialty(turnFight, curObj, ChConfig.HeroSpecialty_SuckHP, relatedSkillID)
|
| | | |
| | | # 结算副本相关的攻击结果,仅主动发起玩家阵容武将触发
|
| | | curPlayer = turnFight.curPlayer
|
| | | if curPlayer and curObj and curObj.GetOwnerID() == curPlayer.GetPlayerID():
|
| | | FBLogic.OnPlayerLineupAttackResult(curPlayer, curObj, killObjList, useSkill, turnFight.mapID, turnFight.funcLineID)
|
| | |
|
| | | effIgnoreObjIDList = missObjIDList + immuneObjIDList
|
| | | # 优先触发本技能额外效果,注:仅该技能释放后该技能的额外效果视为主技能的效果,优先级最高
|
| | | if isUseSkill:
|
| | | __DoCurSkillEff(turnFight, curObj, useSkill, missObjIDList)
|
| | | __DoCurSkillEff(turnFight, curObj, useSkill, effIgnoreObjIDList, isUseSkill)
|
| | |
|
| | | # ========== 以下触发被动 ==========
|
| | |
|
| | |
| | |
|
| | | for tagObj in useSkill.GetTagObjList():
|
| | | tagID = tagObj.GetID()
|
| | | if tagID == curID or tagID in missObjIDList:
|
| | | # 自己或对方闪避了不再触发被动
|
| | | if tagID in effIgnoreObjIDList:
|
| | | continue
|
| | |
|
| | | # 直接攻击
|
| | |
| | | TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_AttackOverDirect, tagObj, connSkill=useSkill)
|
| | | TurnPassive.OnTriggerPassiveEffect(turnFight, tagObj, ChConfig.TriggerWay_BeAttackedDirect, curObj, connSkill=useSkill)
|
| | |
|
| | | # 使用技能后
|
| | | if isUseSkill:
|
| | | TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_UseSkillOver, tagObj, connSkill=useSkill)
|
| | | |
| | | if killObjList:
|
| | | tagObj = killObjList[0]
|
| | | TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_KillOneObj, tagObj, connSkill=useSkill)
|
| | | for tagObj in killObjList:
|
| | | TurnPassive.OnTriggerPassiveEffect(turnFight, curObj, ChConfig.TriggerWay_KillTagObj, tagObj, connSkill=useSkill)
|
| | | |
| | | return
|
| | |
|
| | | def __doCostZhanchui(turnFight, curBatObj, useSkill):
|
| | |
| | | ## 技能释放者怒气相关
|
| | | if SkillCommon.isAngerSkill(useSkill):
|
| | | curBatObj.SetXP(0)
|
| | | elif SkillCommon.isTurnNormalSkill(useSkill):
|
| | | elif SkillCommon.isTurnNormalSkill(useSkill) and useSkill.GetBatType() == ChConfig.TurnBattleType_Normal:
|
| | | addXP = IpyGameDataPY.GetFuncCfg("AngerXP", 3)
|
| | | AddTurnFightXP(curBatObj, addXP, "skillID:%s" % useSkill.GetSkillID())
|
| | | return
|
| | |
| | | GameWorld.DebugLog(" 更新XP: curID=%s,curXP=%s,addXP=%s,updXP=%s,reason=%s" % (gameObj.GetID(), curXP, addXP, updXP, reason))
|
| | | return
|
| | |
|
| | | def __DoCurSkillEff(turnFight, curObj, useSkill, missObjIDList):
|
| | | def DoHeroSpecialty(turnFight, gameObj, specialty, relatedSkillID=0):
|
| | | ## 执行武将特长
|
| | | if gameObj.GetSpecialty() != specialty:
|
| | | return
|
| | | specialtyAddXPDict = IpyGameDataPY.GetFuncEvalCfg("AngerXP", 5, {})
|
| | | if str(specialty) not in specialtyAddXPDict:
|
| | | return
|
| | | addXP = specialtyAddXPDict[str(specialty)]
|
| | | curXP = gameObj.GetXP()
|
| | | updXP = curXP + addXP
|
| | | gameObj.SetXP(updXP, False)
|
| | | GameWorld.DebugLog(" 特长加XP: curID=%s,curXP=%s,addXP=%s,updXP=%s,特性=%s" % (gameObj.GetID(), curXP, addXP, updXP, specialty))
|
| | | Sync_PropertyRefreshView(turnFight, gameObj, ChConfig.AttrID_XP, updXP, addXP, diffType=1, relatedSkillID=relatedSkillID)
|
| | | return
|
| | |
|
| | | def __DoCurSkillEff(turnFight, curObj, useSkill, effIgnoreObjIDList, isUseSkill):
|
| | | ## 执行本技能/buff释放后额外效果
|
| | | for index in xrange(useSkill.GetEffectCount()):
|
| | | curEffect = useSkill.GetEffect(index)
|
| | | if curEffect.GetTriggerWay() != ChConfig.TriggerWay_CurSkillEff:
|
| | | continue
|
| | | |
| | | triggerWay = curEffect.GetTriggerWay()
|
| | | if isUseSkill:
|
| | | if triggerWay != ChConfig.TriggerWay_CurSkillEff:
|
| | | continue
|
| | | else:
|
| | | if triggerWay != ChConfig.TriggerWay_CurSkillEffLst:
|
| | | continue
|
| | | |
| | | effID = curEffect.GetEffectID()
|
| | | GameWorld.DebugLog("执行额外技能效果: %s, missObjIDList=%s" % (effID, missObjIDList))
|
| | | GameWorld.DebugLog("◆执行额外技能效果: %s, triggerWay=%s,effIgnoreObjIDList=%s" % (effID, triggerWay, effIgnoreObjIDList))
|
| | | if effID == 5010:
|
| | | # 额外技能效果
|
| | | __doUseEnhanceSkill(turnFight, curObj, useSkill, curEffect, missObjIDList)
|
| | | __doUseEnhanceSkill(turnFight, curObj, useSkill, curEffect, effIgnoreObjIDList)
|
| | | continue
|
| | |
|
| | | for tagObj in useSkill.GetTagObjList():
|
| | | tagID = tagObj.GetID()
|
| | | GameWorld.DebugLog(" tagID=%s" % (tagID))
|
| | | if tagID in missObjIDList:
|
| | | if tagID in effIgnoreObjIDList:
|
| | | # 闪避了不触发
|
| | | continue
|
| | |
|
| | |
| | |
|
| | | return
|
| | |
|
| | | def __doUseEnhanceSkill(turnFight, curBatObj, useSkill, curEffect, missObjIDList):
|
| | | def __doUseEnhanceSkill(turnFight, curBatObj, useSkill, curEffect, effIgnoreObjIDList):
|
| | | ## 执行主技能的额外技能效果
|
| | | #if useSkill.GetBatType() == ChConfig.TurnBattleType_Enhance:
|
| | | # #GameWorld.DebugLog("自身为额外触发的技能不再触发额外技能! skillID=%s" % useSkill.GetSkillID())
|
| | | # return
|
| | | enhanceSkillID = curEffect.GetEffectValue(0)
|
| | | checkInStateList = curEffect.GetEffectValue(1)
|
| | | if checkInStateList:
|
| | | if isinstance(checkInStateList, int):
|
| | | checkInStateList = [checkInStateList]
|
| | | GameWorld.DebugLog("额外触发的技能: enhanceSkillID=%s,checkInStateList=%s" % (enhanceSkillID, checkInStateList))
|
| | | tagObjList = useSkill.GetTagObjList()
|
| | |
|
| | |
| | | if not tagObj.IsAlive():
|
| | | GameWorld.DebugLog(" 已被击杀不触发: tagID=%s" % (tagID))
|
| | | continue
|
| | | if tagID in missObjIDList:
|
| | | GameWorld.DebugLog(" 闪避的不触发: tagID=%s" % (tagID))
|
| | | if tagID in effIgnoreObjIDList:
|
| | | GameWorld.DebugLog(" 闪避或免疫的不触发: tagID=%s" % (tagID))
|
| | | continue
|
| | | if checkInStateList:
|
| | | inState = False
|
| | | for state in checkInStateList:
|
| | | if tagObj.IsInState(state):
|
| | | inState = True
|
| | | break
|
| | | if not inState:
|
| | | if not tagObj.CheckInState(checkInStateList):
|
| | | GameWorld.DebugLog(" 不在状态下不触发: tagID=%s not in state:%s" % (tagID, checkInStateList))
|
| | | continue
|
| | | if enhanceRate and enhanceRate != ChConfig.Def_MaxRateValue and not GameWorld.CanHappen(enhanceRate, ChConfig.Def_MaxRateValue):
|
| | |
| | | if checkInStateList:
|
| | | inState = False
|
| | | for tagObj in tagObjList:
|
| | | for state in checkInStateList:
|
| | | if not state or tagObj.IsInState(state):
|
| | | inState = True
|
| | | break
|
| | | if inState:
|
| | | if tagObj.CheckInState(checkInStateList):
|
| | | inState = True
|
| | | break
|
| | | if not inState:
|
| | | GameWorld.DebugLog(" 没有目标在状态下不触发: tagObj not in state:%s" % str(checkInStateList))
|
| | |
| | |
|
| | | else:
|
| | | hurtValue, hurtTypes = CalcHurtHP(turnFight, atkObj, defObj, curSkill, atkSkillValue, atkSkillPer)
|
| | | hurtValue, realHurtHP = CalcHurtHPWithBuff(turnFight, atkObj, defObj, curSkill, hurtValue)
|
| | | hurtValue, realHurtHP, hurtTypes = CalcHurtHPWithBuff(turnFight, atkObj, defObj, curSkill, hurtValue, hurtTypes)
|
| | |
|
| | | #伤害结构体
|
| | | hurtObj.SetHurtTypes(hurtTypes)
|
| | |
| | | def CalcHurtHP(turnFight, atkObj, defObj, curSkill, atkSkillValue, atkSkillPer, **kwargs):
|
| | | '''计算伤害,默认按攻击计算
|
| | | '''
|
| | | |
| | | skillID = curSkill.GetSkillID()
|
| | | pmType = GetPMType(atkObj, curSkill)
|
| | | ignoreDef = IsIgnoreDef(curSkill)
|
| | | batType = curSkill.GetBatType()
|
| | |
|
| | | changeHurtType = TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.PassiveEff_ChangeHurtType, curSkill)
|
| | | if changeHurtType == 1:
|
| | | ignoreDef = True
|
| | | GameWorld.DebugLog("强制变更本次伤害为无视防御! skillID=%s" % skillID)
|
| | | |
| | | atkID = atkObj.GetID()
|
| | | defID = defObj.GetID()
|
| | |
|
| | | skillID = curSkill.GetSkillID()
|
| | | calcType = curSkill.GetCalcType() # 伤害计算方式 0-按攻击
|
| | | isTurnNormalSkill = SkillCommon.isTurnNormalSkill(curSkill)
|
| | | isAngerSkill = SkillCommon.isAngerSkill(curSkill)
|
| | | isAtkbackSkill = SkillCommon.isAtkbackSkill(curSkill)
|
| | | isDot = ("damageoftime" in kwargs)
|
| | | angerOverflow = 0 # 怒气溢出值
|
| | |
|
| | | mustHit = False
|
| | | mustHit = False # 是否必命中
|
| | | if isAngerSkill:
|
| | | mustHit = True
|
| | | curXP = atkObj.GetXP()
|
| | | angerOverflow = max(atkObj.GetXP() - IpyGameDataPY.GetFuncCfg("AngerXP", 2), 0)
|
| | | GameWorld.DebugLog("XP必命中! curXP=%s,angerOverflow=%s" % (curXP, angerOverflow))
|
| | |
|
| | | if isDot:
|
| | | mustHit = True
|
| | | |
| | | #命中公式 攻击方类型不同,公式不同
|
| | | if not mustHit:
|
| | | if isTurnNormalSkill and not mustHit:
|
| | | aMissRateDef = atkObj.GetBatAttrValue(ChConfig.AttrID_MissRateDef) #atkObj.GetHit() # 抗闪避率 - 命中
|
| | | dMissRate = defObj.GetBatAttrValue(ChConfig.AttrID_MissRate) # 闪避率
|
| | | missNum = defObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnMissNum)
|
| | | missNum = curSkill.GetTagMissNum(defID)
|
| | | missRate = eval(IpyGameDataPY.GetFuncCompileCfg("MissCfg", 1))
|
| | | if GameWorld.CanHappen(missRate):
|
| | | GameWorld.DebugLog("闪避了! missRate=%s,dMissRate=%s,aMissRateDef=%s,missNum=%s" % (missRate, dMissRate, aMissRateDef, missNum))
|
| | | defObj.SetDict(ChConfig.Def_Obj_Dict_TurnMissNum, missRate + 1)
|
| | | curSkill.SetTagMissNum(defID, missRate + 1)
|
| | | return 0, pow(2, ChConfig.HurtType_Miss)
|
| | |
|
| | | hurtTypes = pow(2, ChConfig.HurtType_Normal)
|
| | |
|
| | | #calcType = curSkill.GetCalcType() 目前暂时按攻击算伤害,之后可以扩展其他
|
| | | |
| | | isSuperHit, isParry = False, False
|
| | | isSuperHit, isParry, isStun = False, False, False
|
| | | aSuperDamPer, dSuperDamPerDef = 0, 0
|
| | | # 暴击(dot除外)
|
| | | if not isDot:
|
| | | isSuperHit = CanSuperHit(atkObj, defObj) # 是否暴击
|
| | | isParry = (isTurnNormalSkill and CanParry(atkObj, defObj)) # 是否格挡,仅针对普攻
|
| | | CanStun(turnFight, atkObj, defObj, curSkill) # 是否击晕
|
| | | isSuperHit = CanSuperHit(turnFight, atkObj, defObj, curSkill) # 是否暴击
|
| | | |
| | | # 闪避、击晕、格挡
|
| | | if isTurnNormalSkill:
|
| | | isParry = CanParry(turnFight, atkObj, defObj, curSkill) # 是否格挡
|
| | | isStun = CanStun(turnFight, atkObj, defObj, curSkill) # 是否击晕
|
| | |
|
| | | if isSuperHit:
|
| | | hurtTypes |= pow(2, ChConfig.HurtType_SuperHit)
|
| | |
| | | if ignoreDef:
|
| | | hurtTypes |= pow(2, ChConfig.HurtType_IgnoreDef)
|
| | |
|
| | | if isStun:
|
| | | hurtTypes |= pow(2, ChConfig.HurtType_Stun)
|
| | | |
| | | #参与运算的数值
|
| | | #rand = random.random() #种子数 0~1
|
| | |
|
| | | aAtk = atkObj.GetBatAttrValue(ChConfig.AttrID_Atk) # 攻击方最大攻击
|
| | | aAtk = atkObj.GetAtk() # 攻击方最大攻击
|
| | |
|
| | | dHP = defObj.GetHP()
|
| | | dDef = 0 if ignoreDef else defObj.GetBatAttrValue(ChConfig.AttrID_Def) # 防守方防御力
|
| | | dDef = 0 if ignoreDef else defObj.GetDef() # 防守方防御力
|
| | | |
| | | atkSkillPer += TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.PassiveEff_AddSkillPer, curSkill)
|
| | |
|
| | | aFinalDamPer = atkObj.GetBatAttrValue(ChConfig.AttrID_FinalDamPer) # 最终加成
|
| | | dFinalDamPerDef = defObj.GetBatAttrValue(ChConfig.AttrID_FinalDamPerDef) # 最终减伤
|
| | |
| | | if isAngerSkill:
|
| | | aAngerSkillPer = atkObj.GetBatAttrValue(ChConfig.AttrID_AngerSkillPer) # 普技增伤
|
| | | dAngerSkillPerDef = defObj.GetBatAttrValue(ChConfig.AttrID_AngerSkillPerDef) # 普技减伤
|
| | | |
| | | |
| | | aAddSkillPer = 0 # 技能增伤
|
| | | aAddSkillPer += TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.AttrID_SkillPer, curSkill)
|
| | | |
| | | # 物法增减伤
|
| | | if pmType == IPY_GameWorld.ghtMag: # 法伤
|
| | | aPMDamPer = atkObj.GetBatAttrValue(ChConfig.AttrID_MagDamPer)
|
| | |
| | | aPMDamPer = atkObj.GetBatAttrValue(ChConfig.AttrID_PhyDamPer)
|
| | | dPMDamPerDef = defObj.GetBatAttrValue(ChConfig.AttrID_PhyDamPerDef)
|
| | |
|
| | | aComboDamPer = 0 # 连击增伤
|
| | | if batType == ChConfig.TurnBattleType_Combo:
|
| | | aComboDamPer = atkObj.GetBatAttrValue(ChConfig.AttrID_ComboDamPer)
|
| | | |
| | | aPursueDamPer = 0 # 追击增伤
|
| | | if batType == ChConfig.TurnBattleType_Pursue:
|
| | | aPursueDamPer = atkObj.GetBatAttrValue(ChConfig.AttrID_PursueDamPer)
|
| | | |
| | | # 所有万分率参数统一除10000.0
|
| | | atkSkillPer /= 10000.0
|
| | | aNormalSkillPer /= 10000.0
|
| | | dNormalSkillPerDef /= 10000.0
|
| | | aAngerSkillPer /= 10000.0
|
| | | dAngerSkillPerDef /= 10000.0
|
| | | aAddSkillPer /= 10000.0
|
| | | aPMDamPer /= 10000.0
|
| | | dPMDamPerDef /= 10000.0
|
| | | aSuperDamPer /= 10000.0
|
| | | dSuperDamPerDef /= 10000.0
|
| | | aFinalDamPer /= 10000.0
|
| | | dFinalDamPerDef /= 10000.0
|
| | | aComboDamPer /= 10000.0
|
| | | aPursueDamPer /= 10000.0
|
| | |
|
| | | GameWorld.DebugLog("伤血计算: atkID=%s,defID=%s,skillID=%s,atkSkillPer=%s,aAtk=%s,dDef=%s,dHP=%s,hurtTypes=%s" |
| | | % (atkID, defID, skillID, atkSkillPer, aAtk, dDef, dHP, hurtTypes))
|
| | | if calcType != ChConfig.Def_Calc_Attack:
|
| | | aAtk = GetCalcBaseValue(calcType, atkObj, defObj)
|
| | | GameWorld.DebugLog("伤血计算: atkID=%s,defID=%s,skillID=%s,atkSkillPer=%s,calcType=%s,aAtk=%s,dDef=%s,dHP=%s,hurtTypes=%s,aAddSkillPer=%s" |
| | | % (atkID, defID, skillID, atkSkillPer, calcType, aAtk, dDef, dHP, hurtTypes, aAddSkillPer))
|
| | |
|
| | | # 持续性伤害
|
| | | if isDot:
|
| | | hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("DOTFormula", 1))
|
| | | GameWorld.DebugLog(" 持续技能伤害=%s" % (hurtValue))
|
| | | elif isTurnNormalSkill:
|
| | | if isTurnNormalSkill:
|
| | | hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 1))
|
| | | GameWorld.DebugLog(" 普攻技能伤害=%s" % (hurtValue))
|
| | | elif isAngerSkill:
|
| | | hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 2))
|
| | | GameWorld.DebugLog(" 怒气技能伤害=%s" % (hurtValue))
|
| | | elif isAtkbackSkill:
|
| | | hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 3))
|
| | | GameWorld.DebugLog(" 反击伤害=%s" % (hurtValue))
|
| | | elif isDot:
|
| | | hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("DOTFormula", 1))
|
| | | GameWorld.DebugLog(" 持续技能伤害=%s" % (hurtValue))
|
| | | elif calcType != ChConfig.Def_Calc_Attack:
|
| | | hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("CalcTypeFormula", 1))
|
| | | GameWorld.DebugLog(" 非按攻击力伤害=%s,calcType=%s,aAtk=%s" % (hurtValue, calcType, aAtk))
|
| | | else:
|
| | | hurtValue = eval(IpyGameDataPY.GetFuncCompileCfg("HurtFormula", 4))
|
| | | GameWorld.DebugLog(" 其他伤害=%s" % (hurtValue))
|
| | |
| | | hurtValue = hurtValue * (1 - parryReduceRatio)
|
| | | GameWorld.DebugLog(" 格挡后伤害=%s,parryReduceRatio=%s" % (hurtValue, parryReduceRatio))
|
| | |
|
| | | hurtValue = max(1, int(hurtValue)) # 负值、保底防范
|
| | | multiValue = TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.PassiveEff_ChangeHurtMulti, curSkill)
|
| | | if multiValue and multiValue != 1:
|
| | | hurtValue = int(hurtValue * multiValue)
|
| | | GameWorld.DebugLog(" 伤害倍值: hurtValue=%s,multiValue=%s" % (hurtValue, multiValue))
|
| | | |
| | | hurtAtkPerMax = curSkill.GetHurtAtkPerMax() # 最大万分比,限制最终伤害不超过攻击力万分率
|
| | | if hurtAtkPerMax:
|
| | | aAtk = atkObj.GetAtk()
|
| | | hurtValueMax = aAtk * hurtAtkPerMax / 10000.0
|
| | | hurtValue = min(hurtValue, hurtValueMax)
|
| | | GameWorld.DebugLog(" 伤害最高限制: hurtValue=%s,hurtAtkPerMax=%s,aAtk=%s" % (hurtValue, hurtAtkPerMax, aAtk))
|
| | | |
| | | hurtValue = max(1, int(hurtValue)) # 负值、保底防范,放最后
|
| | | return hurtValue, hurtTypes
|
| | |
|
| | | def CanSuperHit(atkObj, defObj):
|
| | | def CanSuperHit(turnFight, atkObj, defObj, curSkill):
|
| | | aSuperHitRate = atkObj.GetBatAttrValue(ChConfig.AttrID_SuperHitRate)
|
| | | aSuperHitRate += TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.AttrID_SuperHitRate, curSkill)
|
| | | dSuperHitRateDef = defObj.GetBatAttrValue(ChConfig.AttrID_SuperHitRateDef)
|
| | | happenRate = eval(IpyGameDataPY.GetFuncCompileCfg("SuperHitCfg", 1))
|
| | | if GameWorld.CanHappen(happenRate):
|
| | |
| | |
|
| | | def CanStun(turnFight, atkObj, defObj, curSkill):
|
| | | aStunRate = atkObj.GetBatAttrValue(ChConfig.AttrID_StunRate)
|
| | | aStunRate += TurnPassive.GetTriggerEffectValue(turnFight, atkObj, defObj, ChConfig.AttrID_StunRate, curSkill)
|
| | | dStunRateDef = defObj.GetBatAttrValue(ChConfig.AttrID_StunRateDef)
|
| | | happenRate = eval(IpyGameDataPY.GetFuncCompileCfg("StunCfg", 1))
|
| | | if not GameWorld.CanHappen(happenRate):
|
| | | return False
|
| | | GameWorld.DebugLog("击晕了: happenRate=%s,aStunRate=%s,dStunRateDef=%s" % (happenRate, aStunRate, dStunRateDef))
|
| | | stunSkillID = IpyGameDataPY.GetFuncCfg("StunCfg", 2)
|
| | | TurnBuff.DoAddBuffBySkillID(turnFight, defObj, stunSkillID, atkObj, curSkill, afterLogic=True)
|
| | | return True
|
| | |
|
| | | def CanParry(atkObj, defObj):
|
| | | if defObj.IsInControlled():
|
| | | #被控制无法格挡
|
| | | return False
|
| | | aParryRateDef = atkObj.GetBatAttrValue(ChConfig.AttrID_ParryRateDef)
|
| | | dParryRate = defObj.GetBatAttrValue(ChConfig.AttrID_ParryRate)
|
| | | parryNum = defObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnParryNum)
|
| | | happenRate = eval(IpyGameDataPY.GetFuncCompileCfg("ParryCfg", 1))
|
| | | if GameWorld.CanHappen(happenRate):
|
| | | GameWorld.DebugLog("格挡了: happenRate=%s,aParryRateDef=%s,dParryRate=%s,parryNum=%s" % (happenRate, aParryRateDef, dParryRate, parryNum))
|
| | | defObj.SetDict(ChConfig.Def_Obj_Dict_TurnParryNum, parryNum + 1)
|
| | | if TurnBuff.DoAddBuffBySkillID(turnFight, defObj, stunSkillID, atkObj, curSkill, afterLogic=True):
|
| | | GameWorld.DebugLog("击晕了: happenRate=%s,aStunRate=%s,dStunRateDef=%s" % (happenRate, aStunRate, dStunRateDef))
|
| | | return True
|
| | | return False
|
| | |
|
| | | def CalcHurtHPWithBuff(turnFight, atkObj, defObj, curSkill, hurtValue):
|
| | | def CanParry(turnFight, atkObj, defObj, curSkill):
|
| | | if defObj.IsInControlled():
|
| | | #被控制无法格挡
|
| | | return False
|
| | | |
| | | defID = defObj.GetID()
|
| | | # 格挡印记
|
| | | buffMgr = defObj.GetBuffManager()
|
| | | parryYJBuff = buffMgr.FindBuffByState(ChConfig.BatObjState_ParryYJ)
|
| | | if parryYJBuff and parryYJBuff.GetLayer():
|
| | | GameWorld.DebugLog("格挡印记格挡了: buffID=%s,buffLayer=%s" % (parryYJBuff.GetBuffID(), parryYJBuff.GetLayer()))
|
| | | TurnBuff.DoBuffLayerChange(turnFight, defObj, parryYJBuff, parryYJBuff.GetLayer() - 1, curSkill)
|
| | | return True
|
| | | |
| | | aParryRateDef = atkObj.GetBatAttrValue(ChConfig.AttrID_ParryRateDef)
|
| | | dParryRate = defObj.GetBatAttrValue(ChConfig.AttrID_ParryRate)
|
| | | parryNum = curSkill.GetTagParryNum(defID)
|
| | | happenRate = eval(IpyGameDataPY.GetFuncCompileCfg("ParryCfg", 1))
|
| | | if GameWorld.CanHappen(happenRate):
|
| | | GameWorld.DebugLog("格挡了: happenRate=%s,aParryRateDef=%s,dParryRate=%s,parryNum=%s" % (happenRate, aParryRateDef, dParryRate, parryNum))
|
| | | curSkill.SetTagParryNum(defID, parryNum + 1)
|
| | | return True
|
| | | return False
|
| | |
|
| | | def CalcHurtHPWithBuff(turnFight, atkObj, defObj, curSkill, hurtValue, hurtTypes=0):
|
| | | ## 计算伤害后,因各种buff和状态的影响处理
|
| | | # @return: hurtValue, realHurtHP
|
| | | # @return: hurtValue, realHurtHP, hurtTypes
|
| | |
|
| | | if hurtValue <= 0:
|
| | | return 0, 0
|
| | | return 0, 0, hurtTypes
|
| | | |
| | | if defObj.CheckInState(ChConfig.BatObjState_Wudi):
|
| | | hurtTypes |= pow(2, ChConfig.HurtType_Immune) # 添加免疫
|
| | | buffMgr = defObj.GetBuffManager()
|
| | | for index in range(buffMgr.GetBuffCount()):
|
| | | buff = buffMgr.GetBuffByIndex(index)
|
| | | if not buff:
|
| | | continue
|
| | | skillData = buff.GetSkillData()
|
| | | if skillData.GetCurBuffState() != ChConfig.BatObjState_Wudi:
|
| | | continue
|
| | | # 记录免疫的积攒伤害
|
| | | buffValue = buff.GetValue1() + buff.GetValue2() * ChConfig.Def_PerPointValue
|
| | | updBuffValue = buffValue + hurtValue
|
| | | 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
|
| | |
|
| | | # 减伤盾减伤, 会改变 hurtValue
|
| | | hurtValue = max(0, hurtValue)
|
| | |
| | | buff.SetValue2(updShieldValue / ChConfig.Def_PerPointValue)
|
| | | curSkill.AddAfterLogic(ChConfig.AfterLogic_SyncBuff, [defObj, buff, atkObj, "ReduceShieldValue"])
|
| | |
|
| | | return hurtValue, max(0, realHurtHP)
|
| | | return hurtValue, max(0, realHurtHP), hurtTypes
|
| | |
|
| | | def CalcBounceHP(turnFight, atkObj, defObj, hurtObj, curSkill):
|
| | | '''计算反弹反弹伤害
|
| | |
| | | return
|
| | |
|
| | | GameWorld.DebugLog(" 反弹伤害=%s,%s/%s" % (bounceHP, atkObj.GetHP(), atkObj.GetMaxHP()))
|
| | | bounceHP, realBounceHP = CalcHurtHPWithBuff(turnFight, defObj, atkObj, curSkill, bounceHP)
|
| | | bounceHP, realBounceHP, _ = CalcHurtHPWithBuff(turnFight, defObj, atkObj, curSkill, bounceHP)
|
| | | if bounceHP <= 0:
|
| | | GameWorld.DebugLog(" bounceHP=%s" % (bounceHP))
|
| | | return
|
| | | hurtObj.SetBounceHP(bounceHP)
|
| | |
|
| | |
| | | TurnAttack.AddTurnObjCureHP(atkObj, atkObj, suckHP, cureHP)
|
| | | return
|
| | |
|
| | | def CalcCureHP(userObj, tagObj, curSkill, largeNum=False):
|
| | | def CalcCureHP(turnFight, userObj, tagObj, curSkill, largeNum=False):
|
| | | ''' 计算治疗值
|
| | | '''
|
| | | cureType = curSkill.GetCalcType()
|
| | |
| | |
|
| | | cureBaseValue = GetCalcBaseValue(cureType, userObj, tagObj)
|
| | |
|
| | | #skillPer += PassiveBuffEffMng.GetPassiveSkillValueByTriggerType(userObj, None, curSkill, ChConfig.TriggerType_AddHP)
|
| | | skillPer += TurnPassive.GetTriggerEffectValue(turnFight, userObj, tagObj, ChConfig.PassiveEff_AddSkillPer, curSkill)
|
| | |
|
| | | # 回合制
|
| | | curePer = 0 # 治疗加成
|
| | |
| | | baseValue = curObj.GetAtk()
|
| | | elif calcType == ChConfig.Def_Calc_MaxHP:
|
| | | baseValue = curObj.GetMaxHP()
|
| | | #elif cureType == ChConfig.Def_Calc_HurtValue:
|
| | | # baseValue = GameObj.GetLastHurtValue(userObj)
|
| | | elif calcType == ChConfig.Def_Calc_LastHurt:
|
| | | baseValue = curObj.GetLastHurtValue()
|
| | | elif calcType == ChConfig.Def_Calc_TagMaxHP:
|
| | | baseValue = 0 if not tagObj else tagObj.GetMaxHP()
|
| | | return baseValue
|
| | |
| | | atkObj = buffOwner
|
| | | defObj = batObj
|
| | |
|
| | | atkID = ownerID
|
| | | defID = defObj.GetID()
|
| | | tagObjList = [defObj]
|
| | |
|
| | | poolMgr = ObjPool.GetPoolMgr()
|
| | | useSkill = poolMgr.acquire(BattleObj.PySkill, skillIpyData)
|
| | | useSkill = poolMgr.acquire(BattleObj.PySkill, skillIpyData, atkID)
|
| | | useSkill.SetTagObjList(tagObjList)
|
| | | useSkill.ClearHurtObj()
|
| | | hurtObj = useSkill.AddHurtObj(defID)
|
| | |
|
| | | dHP = defObj.GetHP()
|
| | | GameWorld.DebugLog("结算dot: defID=%s,buffID=%s,skillID=%s,ownerID=%s,hurtValue=%s,hurtTypes=%s,dHP=%s" |
| | | % (defID, buffID, skillID, ownerID, hurtValue, hurtTypes, dHP))
|
| | | hurtValue, realHurtHP = CalcHurtHPWithBuff(turnFight, atkObj, defObj, useSkill, hurtValue)
|
| | | 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))
|
| | | hurtValue, realHurtHP, hurtTypes = CalcHurtHPWithBuff(turnFight, atkObj, defObj, useSkill, hurtValue, hurtTypes)
|
| | |
|
| | | # 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()))
|
| | |
|
| | | hurtObj.SetHurtTypes(hurtTypes)
|
| | |
| | | hurt.SuckHP = min(hurtObj.GetSuckHP(), ChConfig.Def_UpperLimit_DWord)
|
| | | hurt.BounceHP = min(hurtObj.GetBounceHP(), ChConfig.Def_UpperLimit_DWord)
|
| | | clientPack.HurtList.append(hurt)
|
| | | if not clientPack.HurtList:
|
| | | for tagObj in useSkill.GetTagObjList():
|
| | | tagID = tagObj.GetID()
|
| | | hurt = poolMgr.acquire(ChPyNetSendPack.tagSCUseSkillHurt)
|
| | | hurt.ObjID = tagID
|
| | | hurt.CurHP = tagObj.GetHP() % ChConfig.Def_PerPointValue
|
| | | hurt.CurHPEx = tagObj.GetHP() / ChConfig.Def_PerPointValue
|
| | | clientPack.HurtList.append(hurt)
|
| | | |
| | | clientPack.HurtCount = len(clientPack.HurtList)
|
| | | turnFight.addBatPack(clientPack)
|
| | | return
|