| #!/usr/bin/python  | 
| # -*- coding: GBK -*-  | 
| #-------------------------------------------------------------------------------  | 
| #  | 
| #-------------------------------------------------------------------------------  | 
| #  | 
| ##@package AIType_186  | 
| #  | 
| # @todo: NPCͨ¹ý±í¶ÁÈ¡Êͷż¼ÄÜ  | 
| # @author Alee  | 
| # @date 2011-05-18 18:00  | 
| # @version 2.2  | 
| #  | 
| # ÏêϸÃèÊö:  | 
| # @change: "2013-06-18 22:30" Alee Ð¡¹Ö³ðºÞÊýËõС  | 
| # @change: "2014-01-28 15:30" hxp Ôö¼ÓÄêÊÞÊ£ÓàѪÁ¿¹ã²¥  | 
| # @change: "2014-02-21 14:20" hxp Ôö¼ÓÊÀ½çbossÂß¼£¬¹¥»÷»ÆÃû£¬Ê£ÓàѪÁ¿¹ã²¥  | 
| # @change: "2014-02-27 12:00" hxp Ôö¼ÓNPCѪÁ¿°Ù·Ö±ÈÂß¼Èë¿Ú  | 
| # @change: "2014-03-11 19:30" hxp ÐÞ¸ÄÃþÊÀ½çboss»ÆÃûbuffID  | 
| # @change: "2014-04-14 12:00" hxp Ôö¼ÓbossËÀÍöÕÙ»½²É¼¯Îï  | 
| # @change: "2014-09-24 17:00" hxp ÃþÊÀ½çbossbuffÌí¼ÓÂß¼ÐÞ¸Ä  | 
| # @change: "2014-10-27 21:00" hxp ¹¥»÷bossÊÇ·ñ»ñµÃbuff¿ª³öÅäÖà  | 
| # @change: "2014-12-08 21:30" hxp ÓÅ»¯AI  | 
| # @change: "2014-12-15 07:20" Alee ½µµÍNPCAIÏûºÄ  | 
| # @change: "2014-12-31 14:40" Alee ½µµÍNPCAIÏûºÄ  | 
| # @change: "2016-11-22 21:00" hxp Ö§³ÖÌí¼Ó×î´ó³ðºÞbuff  | 
| #------------------------------------------------------------------------------   | 
| #"""Version = 2016-11-22 21:00"""  | 
| #-------------------------------------------------------------------------------  | 
| import ChConfig  | 
| import NPCCommon  | 
| import AICommon  | 
| import IPY_GameWorld  | 
| import AttackCommon  | 
| import GameWorld  | 
| import BaseAttack  | 
| import PlayerState  | 
| import SkillCommon  | 
| import PyGameData  | 
| import BuffSkill  | 
| import GameObj  | 
|   | 
| ## ³õʼ»¯  | 
| #  @param curNPC µ±Ç°npc  | 
| #  @return None  | 
| #  @remarks º¯ÊýÏêϸ˵Ã÷.  | 
| def DoInit(curNPC):  | 
|     angryCount = ChConfig.Def_NormalNPCAngryCount  | 
|     if ChConfig.IsGameBoss(curNPC):  | 
|         angryCount = ChConfig.Def_SuperFBBossAngryCount  | 
|     # ³ðºÞ¹éÊôÒòΪÐèÒª¶ÓԱͬʱ¼Ó³ðºÞ£¬ËùÒÔ³ðºÞÊýÉèÖôóµã  | 
|     if NPCCommon.GetDropOwnerType(curNPC) == ChConfig.DropOwnerType_MaxAngry:  | 
|         angryCount = ChConfig.Def_SuperBossAngryCount  | 
|           | 
|     curNPC.GetNPCAngry().Init(angryCount)  | 
|     return  | 
|   | 
| ## Ö´ÐÐAI  | 
| #  @param curNPC µ±Ç°npc  | 
| #  @param tick µ±Ç°Ê±¼ä  | 
| #  @return None  | 
| #  @remarks º¯ÊýÏêϸ˵Ã÷.  | 
| def ProcessAI(curNPC, tick):  | 
|     npcControl = NPCCommon.NPCControl(curNPC)  | 
|       | 
|     if curNPC.GetCurAction() == IPY_GameWorld.laNPCDie or curNPC.IsAlive() != True:  | 
|         #NPCËÀÍö, ½øÈëËÀÍöµ¹¼ÆÊ±  | 
|         if npcControl.DieTick(tick) == 0:  | 
|             return  | 
|       | 
|     #Ë¢ÐÂ×Ô¼ºµÄbuff  | 
|     if curNPC.GetIsBoss():  | 
|         npcControl.RefreshBuffState(tick)   | 
|         if GameObj.GetHP(curNPC) == 0 :  | 
|             # BUFFË¢ÐÂÖпÉÄܻᵼÖÂNPCËÀÍö  | 
|             return  | 
|     curNPCAction = curNPC.GetCurAction()  | 
|     if curNPCAction == IPY_GameWorld.laNPCMove and curNPC.GetCurMoveType() == IPY_GameWorld.mtRun:  | 
|         AICommon.NormalNPCFast_Move(curNPC, tick)  | 
|         return  | 
|       | 
|     tagObj = __RefreshDropOwner(curNPC, tick)  | 
|       | 
|     if not curNPC.GetIsNeedProcess() or not tagObj:  | 
|         # ÏÈ»ØÑª£¬µÈ»ØÂúÔÙ×öÆäËûÊÂÇé  | 
|         if curNPCAction == IPY_GameWorld.laNPCNull and not npcControl.ProcessHPRestore(tick):  | 
|             AICommon.NormalNPCFree_Move(curNPC, tick)  | 
|         return  | 
|       | 
|     #¹¥»÷Âß¼  | 
|     __NPCFight(curNPC, tagObj, tick)  | 
|       | 
|     npcControl.DoHPPerLogic(ChConfig.Def_NPCHurtTypeAll, 0)  | 
|     return  | 
|   | 
| def __RefreshDropOwner(curNPC, tick, refreshInterval=3000, isDead=False):  | 
|     ## Ë¢ÐÂbossµôÂä¹éÊô  | 
|     # @return: ¿É¹¥»÷µÄµôÂä¹éÊôÄ¿±êÍæ¼Ò  | 
|       | 
|     npcControl = NPCCommon.NPCControl(curNPC)  | 
|     tagObj = None # ¼´½«¹¥»÷µÄÄ¿±ê, ¹éÊô×î´óÉËѪȡ×î´óÉËÑªÍæ¼Ò»ò¶ÓÎé¶ÓÔ±£¬ÆäËûÈ¡×î´ó³ðºÞ  | 
|     ownerType, ownerID = 0, 0  | 
|     dropOwnerType = NPCCommon.GetDropOwnerType(curNPC)  | 
|     if dropOwnerType == ChConfig.DropOwnerType_MaxHurt:  | 
|         maxHurtObj = npcControl.RefreshHurtList(tick, refreshInterval)  | 
|         if maxHurtObj:  | 
|             ownerType, ownerID = maxHurtObj.GetValueType(), maxHurtObj.GetValueID()  | 
|             if ownerType == ChConfig.Def_NPCHurtTypeTeam:  | 
|                 tagObj = __GetMaxHurtTeamPlayer(curNPC, npcControl, ownerID)  | 
|             elif ownerType == ChConfig.Def_NPCHurtTypePlayer:  | 
|                 tagObj = GameWorld.GetObj(ownerID, IPY_GameWorld.gotPlayer)  | 
|                   | 
|     # Ã»Óй¥»÷Ä¿±ê£¬ÔòˢгðºÞ£¬Ö§³ÖÖ÷¶¯¹Ö  | 
|     if not tagObj:  | 
|         angryObjType, maxAngryObj = None, None  | 
|         npcControl.RefreshAngryList(tick, refreshInterval, isUpdAngry=True)  | 
|         maxAngry = npcControl.GetMaxAngryTag()  | 
|         if maxAngry:  | 
|             angryID = maxAngry.GetObjID()  | 
|             angryObjType = maxAngry.GetObjType()  | 
|             #GameWorld.DebugLog("×î´ó³ðºÞÄ¿±ê: ID=%s, Type=%s" % (angryID, angryObjType))  | 
|             maxAngryObj = GameWorld.GetObj(angryID, angryObjType)  | 
|               | 
|         tagObj = maxAngryObj  | 
|         if angryObjType == IPY_GameWorld.gotPlayer and maxAngryObj:  | 
|             teamID = maxAngryObj.GetTeamID()  | 
|             if teamID:  | 
|                 ownerType, ownerID = ChConfig.Def_NPCHurtTypeTeam, teamID  | 
|             else:  | 
|                 ownerType, ownerID = ChConfig.Def_NPCHurtTypePlayer, maxAngryObj.GetPlayerID()  | 
|     __RefreshBossDropOwnerObjBuff(curNPC, npcControl, tick, ownerType, ownerID, isDead)  | 
|     return tagObj  | 
|   | 
| def __GetMaxHurtTeamPlayer(curNPC, npcControl, teamID):  | 
|     ## »ñÈ¡×î´óÉËѪ¶ÓÎéÖй¥»÷µÄÄ¿±ê¶ÓÔ±  | 
|     curTeam = GameWorld.GetTeamManager().FindTeam(teamID)  | 
|     if curTeam:  | 
|         refreshPoint = curNPC.GetRefreshPosAt(curNPC.GetCurRefreshPointIndex())  | 
|         for i in xrange(curTeam.GetMemberCount()):  | 
|             curTeamPlayer = curTeam.GetMember(i)  | 
|             if curTeamPlayer == None or curTeamPlayer.GetPlayerID() == 0:  | 
|                 continue  | 
|             if curTeamPlayer.GetHP() <= 0:  | 
|                 continue  | 
|             if npcControl.GetIsInRefreshPoint(curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), refreshPoint):  | 
|                 return curTeamPlayer  | 
|     return  | 
|   | 
| def __RefreshBossDropOwnerObjBuff(curNPC, npcControl, tick, ownerType=0, ownerID=0, isDead=False):  | 
|     npcID = curNPC.GetNPCID()  | 
|     dropOwnerType = NPCCommon.GetDropOwnerType(curNPC)  | 
|     if dropOwnerType not in [ChConfig.DropOwnerType_MaxHurt, ChConfig.DropOwnerType_MaxAngry]:  | 
|         #GameWorld.DebugLog("²»ÐèҪչʾµôÂä¹éÊôµÄNPC! npcID=%s,dropOwnerType=%s" % (npcID, dropOwnerType))  | 
|         return  | 
|       | 
|     lastDropOwnerID = curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_LastDropOwnerID)  | 
|     lastDropOwnerType = curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_LastDropOwnerType)  | 
|       | 
|     if lastDropOwnerID and (lastDropOwnerType != ownerType or lastDropOwnerID != ownerID):  | 
|         GameWorld.DebugLog("¹éÊô±ä¸ü, Çå³ý¾É¹éÊô! ownerType=%s,ownerID=%s,lastDropOwnerType=%s,lastDropOwnerID=%s"   | 
|                            % (ownerType, ownerID, lastDropOwnerType, lastDropOwnerID))  | 
|         __DelBossDropOwnerBuff(curNPC, lastDropOwnerType, lastDropOwnerID, tick)  | 
|       | 
|     killerDict, curTeam, hurtType, hurtID = {}, None, 0, 0  | 
|       | 
|     # ¸üйéÊô  | 
|     curNPC.SetDict(ChConfig.Def_NPC_Dict_LastDropOwnerID, ownerID)  | 
|     curNPC.SetDict(ChConfig.Def_NPC_Dict_LastDropOwnerType, ownerType)  | 
|       | 
|     # Ë¢Ð¹éÊô  | 
|     if ownerType == ChConfig.Def_NPCHurtTypePlayer:  | 
|         curPlayer = GameWorld.GetObj(ownerID, IPY_GameWorld.gotPlayer)  | 
|         if curPlayer:  | 
|             playerID = curPlayer.GetPlayerID()  | 
|             hurtType, hurtID = ChConfig.Def_NPCHurtTypePlayer, playerID  | 
|             killerDict[playerID] = curPlayer  | 
|             __AddBossDropOwnerPlayerBuff(curPlayer, tick, curNPC)  | 
|               | 
|     elif ownerType == ChConfig.Def_NPCHurtTypeTeam:  | 
|         curTeam = GameWorld.GetTeamManager().FindTeam(ownerID)  | 
|         if not curTeam:  | 
|             return  | 
|           | 
|         # ÒòΪÓл÷ɱ´ÎÊýÏÞÖÆ£¬ËùÒÔ²»ÊÇËùÓеĶÓÔ±¶¼¿ÉÒÔ»ñµÃ¹éÊô£¬ËùÒÔÕâÀïÉèÖÃÎªÌØÊâÖ¸¶¨Íæ¼ÒµôÂä  | 
|         hurtType, hurtID = ChConfig.Def_NPCHurtTypeSpecial, 0  | 
|           | 
|         refreshPoint = curNPC.GetRefreshPosAt(curNPC.GetCurRefreshPointIndex())  | 
|         for i in xrange(curTeam.GetMemberCount()):  | 
|             curTeamPlayer = curTeam.GetMember(i)  | 
|             if curTeamPlayer == None or curTeamPlayer.GetPlayerID() == 0:  | 
|                 continue  | 
|               | 
|             if curTeamPlayer.GetCopyMapID() == GameWorld.GetGameWorld().GetCopyMapID() \  | 
|                 and npcControl.GetIsInRefreshPoint(curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), refreshPoint) \  | 
|                 and AttackCommon.CheckKillNPCByCnt(curTeamPlayer, curNPC, False):  | 
|                 __AddBossDropOwnerPlayerBuff(curTeamPlayer, tick, curNPC)  | 
|                 killerDict[curTeamPlayer.GetPlayerID()] = curTeamPlayer  | 
|                   | 
|             # ²»Í¬Ïß¡¢»òÕß¾àÀ볬³öboss·¶Î§µÄ¶ÓÔ±²»¼Ó¹éÊôbuff  | 
|             else:  | 
|                 isOk = BuffSkill.DelBuffBySkillID(curTeamPlayer, ChConfig.Def_SkillID_DropOwnerBuff, tick, buffOwner=curNPC)  | 
|                 if isOk:  | 
|                     GameWorld.DebugLog("ɾ³ý¹éÊô¶ÓÔ±buff: teamID=%s,playerID=%s" % (ownerID, curTeamPlayer.GetPlayerID()))  | 
|                       | 
|     if isDead:  | 
|         key = (GameWorld.GetGameWorld().GetLineID(), curNPC.GetID(), npcID)  | 
|         teamID = curTeam.GetTeamID() if curTeam else 0  | 
|         if killerDict:  | 
|             PyGameData.g_npcKillerInfo[key] = killerDict, curTeam, hurtType, hurtID  | 
|         GameWorld.Log("Boss±»»÷ɱ: npcID=%s,key=%s,playerIDList=%s,teamID=%s" % (npcID, key, killerDict.keys(), teamID))  | 
|     return  | 
|   | 
| def __AddBossDropOwnerPlayerBuff(curPlayer, tick, curNPC):  | 
|     findBuff = SkillCommon.FindBuffByID(curPlayer, ChConfig.Def_SkillID_DropOwnerBuff)[0]  | 
|     if not findBuff:  | 
|         SkillCommon.AddBuffBySkillType_NoRefurbish(curPlayer, ChConfig.Def_SkillID_DropOwnerBuff, tick, buffOwner=curNPC)  | 
|         GameWorld.DebugLog("Ìí¼Ó¹éÊôbuff: playerID=%s" % curPlayer.GetPlayerID())  | 
|     return  | 
|   | 
| def __DelBossDropOwnerBuff(curNPC, ownerType, ownerID, tick):  | 
|       | 
|     if ownerType == ChConfig.Def_NPCHurtTypePlayer:  | 
|         curPlayer = GameWorld.GetObj(ownerID, IPY_GameWorld.gotPlayer)  | 
|         if not curPlayer:  | 
|             return  | 
|         GameWorld.DebugLog("ɾ³ý¹éÊôÍæ¼Òbuff: playerID=%s" % (ownerID))  | 
|         BuffSkill.DelBuffBySkillID(curPlayer, ChConfig.Def_SkillID_DropOwnerBuff, tick, buffOwner=curNPC)  | 
|           | 
|     elif ownerType == ChConfig.Def_NPCHurtTypeTeam:  | 
|         curTeam = GameWorld.GetTeamManager().FindTeam(ownerID)  | 
|         if not curTeam:  | 
|             return  | 
|         GameWorld.DebugLog("ɾ³ý¹éÊô¶ÓÎébuff: teamID=%s" % (ownerID))  | 
|         for i in xrange(curTeam.GetMemberCount()):  | 
|             curTeamPlayer = curTeam.GetMember(i)  | 
|             if curTeamPlayer == None or curTeamPlayer.GetPlayerID() == 0:  | 
|                 continue  | 
|             BuffSkill.DelBuffBySkillID(curTeamPlayer, ChConfig.Def_SkillID_DropOwnerBuff, tick, buffOwner=curNPC)  | 
|     return  | 
|   | 
| def __DelayBossDropOwnerBuff(curNPC):  | 
|     ''' ÑÓ³ÙbossµôÂä¹éÊôbuffÏûʧʱ¼ä '''  | 
|       | 
|     ownerID = curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_LastDropOwnerID)  | 
|     ownerType = curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_LastDropOwnerType)  | 
|       | 
|     if ownerType == ChConfig.Def_NPCHurtTypePlayer:  | 
|         curPlayer = GameWorld.GetObj(ownerID, IPY_GameWorld.gotPlayer)  | 
|         if not curPlayer:  | 
|             return  | 
|         __SetBossDropOwnerBuffDisappearTime(curPlayer, curNPC)  | 
|           | 
|     elif ownerType == ChConfig.Def_NPCHurtTypeTeam:  | 
|         curTeam = GameWorld.GetTeamManager().FindTeam(ownerID)  | 
|         if not curTeam:  | 
|             return  | 
|         for i in xrange(curTeam.GetMemberCount()):  | 
|             curTeamPlayer = curTeam.GetMember(i)  | 
|             if curTeamPlayer == None or curTeamPlayer.GetPlayerID() == 0:  | 
|                 continue  | 
|             __SetBossDropOwnerBuffDisappearTime(curTeamPlayer, curNPC)  | 
|     return  | 
|   | 
| def __SetBossDropOwnerBuffDisappearTime(curPlayer, curNPC):  | 
|       | 
|     findSkill = GameWorld.GetGameData().GetSkillBySkillID(ChConfig.Def_SkillID_DropOwnerBuff)  | 
|     if not findSkill:  | 
|         return  | 
|       | 
|     buffType = SkillCommon.GetBuffType(findSkill)  | 
|     buffTuple = SkillCommon.GetBuffManagerByBuffType(curPlayer, buffType)  | 
|     if buffTuple == ():  | 
|         return  | 
|       | 
|     RemainTime = 10000 # ÑÓ³Ù10ÃëÏûʧ  | 
|     tick = GameWorld.GetGameWorld().GetTick()  | 
|       | 
|     buffStateManager = buffTuple[0]  | 
|     for index in xrange(buffStateManager.GetBuffCount()):  | 
|         curBuff = buffStateManager.GetBuff(index)  | 
|         buffSkill = curBuff.GetSkill()  | 
|           | 
|         if buffSkill.GetSkillTypeID() != ChConfig.Def_SkillID_DropOwnerBuff:  | 
|             continue  | 
|           | 
|         if curNPC.GetID() != curBuff.GetOwnerID():  | 
|             #GameWorld.DebugLog("·Çbuff¹éÊô×Å£¬²»ÉèÖÃÏûʧʱ¼ä£¡", curPlayer.GetPlayerID())  | 
|             break  | 
|           | 
|         curBuff.SetCalcStartTick(tick)   | 
|         curBuff.SetRemainTime(RemainTime)  | 
|           | 
|         # Í¨ÖªbuffˢР | 
|         buffStateManager.Sync_RefreshBuff(index, curBuff.GetRemainTime())  | 
|         #GameWorld.DebugLog("µôÂä¹éÊôbuffÏûʧʱ¼ä: RemainTime=%s" % (RemainTime), curPlayer.GetPlayerID())  | 
|         break  | 
|     return  | 
|   | 
| ## Ã¿´Î±»¹¥»÷´¦Àí½á¹û  | 
| #  @param atkObj ¹¥»÷·¢ÆðÕß  | 
| #  @param defObj ±»¹¥»÷Õß  | 
| #  @param skill ¹¥»÷¼¼ÄÜ  | 
| #  @param tick   | 
| #  @return ¾ßÌåÉ˺¦Öµ  | 
| def OnAttacked(atkObj, curNPC, skill, tick):  | 
|     if atkObj.GetGameObjType() == IPY_GameWorld.gotPlayer:  | 
|         PlayerState.SetBossStateTick(atkObj, tick)  | 
|     return  | 
|   | 
| ## NPC±»Íæ¼ÒɱËÀ  | 
| def OnAttackDieByPlayer(curNPC, curPlayer, skill):  | 
|     tick = GameWorld.GetGameWorld().GetTick()  | 
|     PlayerState.SetBossStateTick(curPlayer, tick)  | 
|       | 
|     #±»»÷É±Ê±Ç¿ÖÆË¢Ð¹éÊô  | 
|     __RefreshDropOwner(curNPC, tick, 0, True)  | 
|     return  | 
|   | 
| ## NPCËÀÍö´¦Àí  | 
| #  @param curNPC ËÀÍöNPC  | 
| #  @param HurtType µôÂäÀàÐÍ  | 
| #  @param HurtID ¶ÔÓ¦ÓµÓÐÕßid  | 
| #  @param modulus µôÂäϵÊý  | 
| #  @return None  | 
| def OnDie(curNPC, HurtType, HurtID):  | 
|     AICommon.DoNPCUseSkillOnDie(curNPC)  | 
|     __DelayBossDropOwnerBuff(curNPC)  | 
|     return  | 
|       | 
| ## npc¹¥»÷Âß¼  | 
| #  @param curNPC µ±Ç°npc  | 
| #  @param tagID curNPCAngryID  | 
| #  @param tagType curNPCAngryType   | 
| #  @param tick µ±Ç°Ê±¼ä  | 
| #  @return None  | 
| #  @remarks º¯ÊýÏêϸ˵Ã÷.     | 
| def __NPCFight(curNPC, curTag, tick):  | 
|     #Ô¤¾¯´¦Àí  | 
|     if AICommon.DoNPCSkillWarningProcess(curNPC, tick):  | 
|         return  | 
|       | 
|     #ÉèÖýøÈëÕ½¶·×´Ì¬  | 
|     NPCCommon.SetNPCInBattleState(curNPC)  | 
|       | 
|     npcControl = NPCCommon.NPCControl(curNPC)  | 
|       | 
|     #²»ÔÚÒÆ¶¯·¶Î§  | 
|     if npcControl.IsInRefreshArea() != True:   | 
|         #×·»÷·µ»Ø  | 
|         npcControl.MoveBack()  | 
|         return  | 
|       | 
|     #¿ªÊ¼¹¥»÷  | 
|     if curTag == None or GameObj.GetHP(curTag) <= 0:  | 
|         return  | 
|       | 
|     tagDist = GameWorld.GetDist(curNPC.GetPosX(), curNPC.GetPosY(), curTag.GetPosX(), curTag.GetPosY())  | 
|       | 
|     #---ÓÅÏÈÊͷż¼ÄÜ---  | 
|     if AICommon.DoAutoUseSkill(curNPC, curTag, tagDist, tick):  | 
|         return  | 
|       | 
|     #---ÊÍ·ÅÆÕͨ¹¥»÷---  | 
|       | 
|     #³¬¹ý¹¥»÷¾àÀë,ÒÆ¶¯¹ýÈ¥  | 
|     if tagDist > curNPC.GetAtkDist():  | 
|         npcControl.MoveToObj_Detel(curTag)  | 
|         return  | 
|       | 
|     if tick - curNPC.GetAttackTick() < curNPC.GetAtkInterval():  | 
|         #¹¥»÷¼ä¸ôûÓе½, ·µ»Ø  | 
|         return  | 
|       | 
|     if npcControl.FixTagPos(curTag.GetPosX(), curTag.GetPosY()):  | 
|         #ÐÞÕýÕâ¸öNPCµÄÕ¾Á¢Î»Öà  | 
|         return  | 
|       | 
|     #ÆÕͨ¹¥»÷  | 
|     BaseAttack.Attack(curNPC, curTag, None, tick)  | 
|     return  | 
|   |