From 776cf3759b9801f3795ee975cd77078f505b90d6 Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期六, 06 一月 2024 14:49:08 +0800
Subject: [PATCH] 10071 【后端】灵宠改版

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py                 |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerPet.py                |  510 ++++++--------------
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py                |   28 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AICommon.py              |   17 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py               |  184 ++++--
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py |    6 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py              |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py                   |    6 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PetControl.py               |  582 ++++-------------------
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py                 |   16 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py                   |   37 +
 PySysDB/PySysDBPY.h                                                                                    |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py            |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                        |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py      |   60 +-
 15 files changed, 478 insertions(+), 978 deletions(-)

diff --git a/PySysDB/PySysDBPY.h b/PySysDB/PySysDBPY.h
index 1fd6f88..fff634f 100644
--- a/PySysDB/PySysDBPY.h
+++ b/PySysDB/PySysDBPY.h
@@ -882,7 +882,7 @@
 	DWORD		_DataMapID;	//数据地图ID
 	WORD		_LineID;	//功能线路ID
 	DWORD		NPCID;	//NPCID
-	DWORD		SummerNPCID;	//召唤兽ID
+	list		PetNPCIDList;	//灵宠NPCID列表
 	list		AwardItemListFirst;	//首次过关奖励列表[[物品ID,个数,是否拍品], ...]
 	list		AwardItemList;	//再次过关奖励列表[[物品ID,个数,是否拍品], ...]
 };
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py
index a40755f..d1a33bc 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py
@@ -2114,6 +2114,8 @@
         fbFightPower = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.FBPD_HelpBattleFBFightPower)
         fbBaseHurt = GameWorld.GetGameFB().GetGameFBDictByKey(ChConfig.FBPD_HelpBattleFBBaseHurt)
         helpBattleFormatKey = "HelpRobot_Def"
+    if atkObjType == IPY_GameWorld.gotNPC and PetControl.IsPetNPC(atkObj):
+        mustHit = True
         
     #命中公式 攻击方类型不同,公式不同
     hitFormula = ReadChConfig.GetChConfig('CalcCanHit')
@@ -2465,6 +2467,8 @@
             return "HelpRobot"
         if obj.GetType() in ChConfig.PVPNPCTypeList:
             return "P"
+        if obj.GetType() == IPY_GameWorld.ntPet:
+            return "Pet"
         
     objType = obj.GetGameNPCObjType()
     if objType == IPY_GameWorld.gnotPet:
@@ -2476,7 +2480,7 @@
 # @param None
 # @return 攻击形式字符串
 def GetAtkState(atkObj, defObj):
-    isPet = PetControl.IsPet(atkObj)
+    isPet = PetControl.IsPetNPC(atkObj)
     if IsPVENPCObj(atkObj) or IsPVENPCObj(defObj):
         return 'PVE_%s' if not isPet else 'PetVE_%s'
     
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
index 6d0ff3c..76e6386 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
@@ -29,6 +29,7 @@
 import NPCCommon
 import GameWorld
 import GameObj
+import PetControl
 
 (
 FightState_Start,
@@ -37,6 +38,12 @@
 FightState_Fail,
 FightState_Over,
 ) = range(5)
+
+def GetObjName(gameObj):
+    objName = gameObj.GetName()
+    faction = GameObj.GetFaction(gameObj)
+    fightPlaceNum = gameObj.GetDictByKey(ChConfig.Def_Obj_Dict_FightPetPlaceNum)
+    return "%s%s %s" % ("A" if faction == 1 else "B", fightPlaceNum, objName)
 
 #// B4 10 回合制战斗 #tagCMTurnFight
 #
@@ -59,7 +66,7 @@
     SyncTurnFightState(curPlayer, mapID, funcLineID, tagPlayerID, FightState_Start)
     
     if tagPlayerID:
-        PlayerViewCacheTube.GetPlayerPropDataCall(curPlayer, tagPlayerID, DoTrunFightVSPlayer, [mapID, funcLineID])
+        PlayerViewCacheTube.GetPlayerPropDataCall(curPlayer, tagPlayerID, DoTrunFightVSPlayer, [mapID, funcLineID], True)
         return
     
     DoTrunFight(curPlayer, mapID, funcLineID, tagPlayerID, tick)
@@ -78,28 +85,16 @@
 def DoTrunFight(curPlayer, mapID, funcLineID, tagPlayerID, tick):
     playerID = curPlayer.GetPlayerID()
     posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
-    
-    factionListA, factionListB = [], []
-    factionListA.append(curPlayer)
-    curPet = curPlayer.GetPetMgr().GetFightPet()
-    curPet and factionListA.append(curPet)
-    
-    # 玩家阵营的其他战斗实例,可扩展...
-    assistNPCID = 0 # 协助召唤兽
-    if assistNPCID:
-        assistNPC = NPCCommon.SummonNPC(curPlayer, assistNPCID, posX, posY)
-        assistNPC and factionListA.append(assistNPC)
-        
+    GameWorld.DebugLog("===== 执行回合制战斗: mapID=%s,funcLineID=%s,tagPlayerID=%s" % (mapID, funcLineID, tagPlayerID), playerID)
     tagObj = None
     if tagPlayerID:
         npcID = ChConfig.Def_NPCID_PVP
         tagObj = NPCCommon.SummonMapNpc(npcID, posX, posY, sightLevel=playerID, pvpPlayerID=tagPlayerID)
         if not tagObj:
             return
-        factionListB.append(tagObj)
-        
-        # 对手玩家镜像的其他战斗实例...
-        
+        PlusData = PlayerViewCacheTube.GetPlayerPropPlusDictByID(playerID)[1] # 从缓存中获取
+        petCacheInfo = PlusData.get("Pet")
+        fightPetObjListB = PetControl.CalloutFightPet(tagObj, petCacheInfo)
     else:
         ipyData = IpyGameDataPY.GetIpyGameData("FBTurn", mapID, funcLineID)
         if not ipyData:
@@ -110,34 +105,42 @@
         tagObj = NPCCommon.SummonMapNpc(npcID, posX, posY, sightLevel=playerID)
         if not tagObj:
             return
-        factionListB.append(tagObj)
-        summerNPCID = ipyData.GetSummerNPCID()
-        if summerNPCID:
-            summerNPC = NPCCommon.SummonNPC(tagObj, summerNPCID, posX, posY)
-            summerNPC and factionListB.append(summerNPC)
-            
-    if not factionListB:
-        return
-    
-    for gameObj in factionListA:
-        GameObj.SetFaction(gameObj, 1)
-    for gameObj in factionListB:
-        GameObj.SetFaction(gameObj, 2)
+        petNPCIDList = ipyData.GetPetNPCIDList()
+        petCacheInfo = [] # 从配表中读取组合,技能默认取NPC表配置的
+        for state, petNPCID in enumerate(petNPCIDList, 1):
+            petCacheInfo.append({"npcID":petNPCID, "state":state, "quality":0})
+        fightPetObjListB = PetControl.CalloutFightPet(tagObj, petCacheInfo)
         
-    #一个回合攻击顺序,由攻击速度决定,攻速相同下阵营1先攻击,还相同则由ID决定
-    fightObjList = factionListA + factionListB
-    fightObjList.sort(key=lambda o: (GameObj.GetAtkSpeed(o), (10 - GameObj.GetFaction(o)), o.GetID()), reverse=True)
-    
     turnMax = IpyGameDataPY.GetFuncCfg("TurnFight", 1)
-    GameWorld.DebugLog("===== 执行回合制战斗: mapID=%s,funcLineID=%s,tagPlayerID=%s,tagObjID=%s" 
-                       % (mapID, funcLineID, tagPlayerID, tagObj.GetID()), playerID)
-    # 战斗前初始化
-    for gameObj in fightObjList:
-        TurnFightObjStartInit(gameObj)
-        
+    # 宠物先攻击
+    fightPetObjListA = PetControl.CalloutFightPet(curPlayer)
+    factionListA = fightPetObjListA + [curPlayer]
+    factionListB = fightPetObjListB + [tagObj]
+    atkFactionList = [factionListA, factionListB]
+    
+    # 战斗前初始化,可能会改变攻速,所以先初始化
+    for faction, factionObjList in enumerate(atkFactionList, 1):
+        for gameObj in factionObjList:
+            TurnFightObjStartInit(gameObj, faction, tick)
+            
+    #一个回合攻击顺序,为了后续逻辑统一,都是先确定好顺序
+    sortType = 2 # 攻击顺序排序方式
+    fightObjList = []
+    #方式1: 纯由攻击速度决定,攻速相同下阵营1先攻击,还相同则由ID决定
+    if sortType == 1:
+        fightObjList = factionListA + factionListB
+        fightObjList.sort(key=lambda o: (GameObj.GetAtkSpeed(o), (10 - GameObj.GetFaction(o)), o.GetID()), reverse=True)
+    #方式2:根据阵营主角攻速决定先手,然后每个阵营轮流固定位置攻击
+    elif sortType == 2:
+        if GameObj.GetAtkSpeed(curPlayer) < GameObj.GetAtkSpeed(tagObj):
+            atkFactionList = [factionListB, factionListA]
+        for i in range(len(factionListA)):
+            for factionObjList in atkFactionList:
+                fightObjList.append(factionObjList[i])
+                
     isWin = None
     for turnNum in range(1, turnMax + 1):
-        GameWorld.DebugLog("----- 回合制战斗轮次: %s -----" % turnNum, playerID)
+        GameWorld.DebugLog("【----- 回合制战斗轮次: %s -----】" % turnNum)
         SyncTurnFightState(curPlayer, mapID, funcLineID, tagPlayerID, FightState_Fighting, turnNum, turnMax)
         
         # 回合开始: 做一些每回合重置逻辑或者某些根据回合触发的效果等
@@ -145,18 +148,22 @@
             TurnFightObjPerTurnStart(gameObj, turnNum, tick)
             
         # 回合战斗: 轮流依次攻击
-        for actNum, gameObj in enumerate(fightObjList, 1):
-            if not gameObj:
+        for gameObj in fightObjList:
+            if not gameObj or GameObj.GetHP(gameObj) <= 0:
                 continue
             faction = GameObj.GetFaction(gameObj)
-            tagGameObj = tagObj if faction == 1 else curPlayer
             objType = gameObj.GetGameObjType()
             objID = gameObj.GetID()
+            objName = GetObjName(gameObj)
+            curHP = GameObj.GetHP(gameObj)
+            
+            tagGameObj = tagObj if faction == 1 else curPlayer
             tagObjType = tagGameObj.GetGameObjType()
             tagObjID = tagGameObj.GetID()
+            tagHP = GameObj.GetHP(tagGameObj)
             
-            GameWorld.DebugLog("    行动: turnNum=%s,actNum=%s,faction=%s,objType=%s,objID=%s,tagObjType=%s,tagObjID=%s" 
-                               % (turnNum, actNum, faction, objType, objID, tagObjType, tagObjID))
+            GameWorld.DebugLog("    ★回合%s %s 行动 : objType-ID-HP(%s-%s-%s), tagType-ID-HP(%s-%s-%s)" 
+                               % (turnNum, objName, objType, objID, curHP, tagObjType, tagObjID, tagHP))
             SyncTurnFightObjAction(curPlayer, turnNum, objType, objID)
             
             DoAttack(gameObj, tagGameObj, tick)
@@ -190,22 +197,25 @@
                        % (mapID, funcLineID, tagPlayerID, isWin, overState), playerID)
     return
 
-def TurnFightObjStartInit(gameObj):
+def TurnFightObjStartInit(gameObj, faction, tick):
     ## 回合制战斗实例初始化
     if not gameObj:
         return
-    
     gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnFightNum, 1)
+    GameObj.SetFaction(gameObj, faction)
     GameObj.SetHPFull(gameObj)
     gameObj.RefreshView()
     
     objType = gameObj.GetGameObjType()
     npcID = gameObj.GetNPCID() if objType == IPY_GameWorld.gotNPC else 0
-    GameWorld.DebugLog("初始化实例: objID=%s,npcID=%s,faction=%s,atkSpeed=%s,HP=%s,Atk=%s,Def=%s" 
-                       % (gameObj.GetID(), npcID, GameObj.GetFaction(gameObj), GameObj.GetAtkSpeed(gameObj), GameObj.GetHP(gameObj), gameObj.GetMaxAtk(), gameObj.GetDef()))
-    GameWorld.DebugLog("    闪命(%s,%s),闪避(%s,%s),暴击(%s,%s),击晕(%s,%s),连击(%s,%s),反击(%s,%s),吸血(%s,%s)" 
-                       % (gameObj.GetMiss(), gameObj.GetHit(),
-                          GameObj.GetMissRate(gameObj), GameObj.GetMissDefRate(gameObj),
+    objName = GetObjName(gameObj)
+    fightPlaceNum = gameObj.GetDictByKey(ChConfig.Def_Obj_Dict_FightPetPlaceNum)
+    
+    GameWorld.DebugLog("【 %s 初始化 %s 】 objID=%s,npcID=%s,atkSpeed=%s,HP=%s,Atk=%s,Def=%s" 
+                       % (objName, BaseAttack.GetObjAttackName(gameObj), gameObj.GetID(), npcID, 
+                          GameObj.GetAtkSpeed(gameObj), GameObj.GetHP(gameObj), gameObj.GetMaxAtk(), gameObj.GetDef()))
+    GameWorld.DebugLog("    闪避(%s,%s),暴击(%s,%s),击晕(%s,%s),连击(%s,%s),反击(%s,%s),吸血(%s,%s)" 
+                       % (GameObj.GetMissRate(gameObj), GameObj.GetMissDefRate(gameObj),
                           GameObj.GetSuperHitRate(gameObj), GameObj.GetSuperHitRateReduce(gameObj),
                           GameObj.GetFaintRate(gameObj), GameObj.GetFaintDefRate(gameObj),
                           GameObj.GetComboRate(gameObj), GameObj.GetComboDefRate(gameObj),
@@ -217,11 +227,23 @@
     if objType == IPY_GameWorld.gotPlayer:            
         skillManager = gameObj.GetSkillManager()
         for i in range(skillManager.GetSkillCount()):
-            skill = skillManager.GetSkillByIndex(i)
-            skill.SetRemainTime(0)
+            curSkill = skillManager.GetSkillByIndex(i)
+            curSkill.SetRemainTime(0)
             
     elif objType == IPY_GameWorld.gotNPC:
-        pass
+        skillIDList = []
+        skillManager = gameObj.GetSkillManager()
+        for i in range(skillManager.GetSkillCount()):
+            curSkill = skillManager.GetSkillByIndex(i)
+            if not curSkill:
+                continue
+            skillIDList.append(curSkill.GetSkillID())
+            if fightPlaceNum:
+                # 灵宠技能开始时直接进入CD
+                curSkill.SetLastUseTick(tick)
+                curSkill.SetRemainTime(curSkill.GetCoolDownTime())
+        GameWorld.DebugLog("    NPC技能: npcID=%s,skillIDList=%s" % (npcID, skillIDList))
+        
     return
 
 def TurnFightObjPerTurnStart(gameObj, turnNum, tick):
@@ -237,7 +259,8 @@
     
     objType = gameObj.GetGameObjType()
     objID = gameObj.GetID()
-    GameWorld.DebugLog("ObjPerTurnStart: faction=%s,objType=%s,objID=%s,turnNum=%s,HP=%s" % (GameObj.GetFaction(gameObj), objType, objID, turnNum, GameObj.GetHP(gameObj)))
+    objName = GetObjName(gameObj)
+    GameWorld.DebugLog("ObjPerTurnStart: 回合%s, %s objType-ID-HP(%s-%s-%s)" % (turnNum, objName, objType, objID, GameObj.GetHP(gameObj)))
     
     # 每回合开始减技能CD
     skillManager = gameObj.GetSkillManager()
@@ -357,7 +380,9 @@
 
 def CanAtkBack(atkObj, defObj):
     ## 可否反击
-    
+    if atkObj.GetDictByKey(ChConfig.Def_Obj_Dict_FightPetPlaceNum) > 0:
+        #GameWorld.DebugLog("            被灵宠攻击时无法触发反击")
+        return False
     defAtkBackRate = GameObj.GetAtkBackRate(defObj) # 防方反击率
     atkBackNum = defObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnAtkBackNum) # 已反击次数
     if atkBackNum > 10:
@@ -407,18 +432,40 @@
     ## NPC攻击
     if not curObj:
         return
+    
+    objName = "● %s" % GetObjName(curObj)
+    #有值代表灵宠上阵位置,判断是否有概率攻击
+    fightPlaceNum = curObj.GetDictByKey(ChConfig.Def_Obj_Dict_FightPetPlaceNum)
+    if fightPlaceNum > 0:
+        fightRateList = IpyGameDataPY.GetFuncEvalCfg("PetGoOutFight", 2)
+        if fightPlaceNum > len(fightRateList):
+            return
+        fightRatePer = fightRateList[fightPlaceNum - 1]
+        
+        qualityAddRateInfo = IpyGameDataPY.GetFuncEvalCfg("PetGoOutFight", 3, {})
+        petQuality = curObj.GetDictByKey(ChConfig.Def_Obj_Dict_FightPetQuality)
+        qualityAddPer = qualityAddRateInfo.get(str(petQuality), 0)
+        fightRatePer += qualityAddPer
+        if fightRatePer < 100 and not GameWorld.CanHappen(fightRatePer, 100):
+            GameWorld.DebugLog("        灵宠概率不攻击: curID=%s,fightPlaceNum=%s,petQuality=%s,fightRatePer=%s" 
+                               % (curObj.GetID(), fightPlaceNum, petQuality, fightRatePer))
+            return
+        
     tagDist = 0
     atkOK = AICommon.DoAutoUseSkill(curObj, tagObj, tagDist, tick)
     #---优先释放技能---
     if not atkOK:
-        #普通攻击
-        atkOK = BaseAttack.Attack(curObj, tagObj, None, tick)
-        if atkOK:
-            GameWorld.DebugLog("        NPC普通攻击: curID=%s,tagID=%s,atkOK=%s" % (curObj.GetID(), tagObj.GetID(), atkOK))
+        if fightPlaceNum:
+            GameWorld.DebugLog("        灵宠不可普通攻击: curID=%s,fightPlaceNum=%s" % (curObj.GetID(), fightPlaceNum))
         else:
-            GameWorld.DebugLog("        NPC攻击失败: curID=%s,tagID=%s,atkOK=%s" % (curObj.GetID(), tagObj.GetID(), atkOK))
+            #普通攻击
+            atkOK = BaseAttack.Attack(curObj, tagObj, None, tick)
+            if atkOK:
+                GameWorld.DebugLog("        %s 普通攻击: curID=%s,tagID=%s,atkOK=%s" % (objName, curObj.GetID(), tagObj.GetID(), atkOK))
+            else:
+                GameWorld.DebugLog("        %s 攻击失败: curID=%s,tagID=%s,atkOK=%s" % (objName, curObj.GetID(), tagObj.GetID(), atkOK))
     else:
-        GameWorld.DebugLog("        NPC技能攻击: curID=%s,tagID=%s,atkOK=%s" % (curObj.GetID(), tagObj.GetID(), atkOK))
+        GameWorld.DebugLog("        %s 技能攻击: curID=%s,tagID=%s,atkOK=%s" % (objName, curObj.GetID(), tagObj.GetID(), atkOK))
     return atkOK
 
 def PlayerAttack(curPlayer, tagObj, tick):
@@ -433,6 +480,7 @@
     posX, posY = tagPosX, tagPosY = curPlayer.GetPosX(), curPlayer.GetPosY()
     tagObjType, tagObjID = tagObj.GetGameObjType(), tagObj.GetID()
     
+    objName = "● %s" % GetObjName(curPlayer)
     curPlayer.ClearUseSkillRec()
     curPlayer.SetAttackTargetPos(posX, posY)
     curPlayer.SetUseSkillPosX(tagPosX)
@@ -461,10 +509,10 @@
         curPlayer.SetUseSkill(curSkill.GetSkillData())
         useSkillData = curPlayer.GetUseSkill()
         if not PlayerState.__DoClientUseSkillEx(curPlayer, useSkillData, tick):
-            GameWorld.DebugLog("        玩家技能攻击失败: playerID=%s,tagID=%s,skillID=%s" % (playerID, tagObjID, skillID))
+            GameWorld.DebugLog("        %s 技能攻击失败: playerID=%s,tagID=%s,skillID=%s" % (objName, playerID, tagObjID, skillID))
             continue
         useSkillResult = True
-        GameWorld.DebugLog("        玩家技能攻击成功: playerID=%s,tagID=%s,skillID=%s" % (playerID, tagObjID, skillID))
+        GameWorld.DebugLog("        %s 技能攻击成功: playerID=%s,tagID=%s,skillID=%s" % (objName, playerID, tagObjID, skillID))
         
         break
     
@@ -475,9 +523,9 @@
         atkOK = BaseAttack.Attack(curPlayer, tagObj, None, tick)
         if atkOK:
             useSkillResult = True
-            GameWorld.DebugLog("        玩家普通攻击: curID=%s,tagID=%s,atkOK=%s" % (playerID, tagObjID, atkOK))
+            GameWorld.DebugLog("        %s 普通攻击: curID=%s,tagID=%s,atkOK=%s" % (objName, playerID, tagObjID, atkOK))
         else:
-            GameWorld.DebugLog("        玩家攻击失败: curID=%s,tagID=%s,atkOK=%s" % (playerID, tagObjID, atkOK))
+            GameWorld.DebugLog("        %s 攻击失败: curID=%s,tagID=%s,atkOK=%s" % (objName, playerID, tagObjID, atkOK))
     return useSkillResult
 
 def SyncTurnFightState(curPlayer, mapID, funcLineID, tagPlayerID, state, turnNum=0, turnMax=0):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index 165ff53..1ecceb5 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -3070,6 +3070,8 @@
 Def_Obj_Dict_TurnComboNum = 'TurnComboNum' # 本回合已累计连击次数
 Def_Obj_Dict_TurnAtkBackNum = 'TurnAtkBackNum' # 本回合已累计反击次数
 Def_Obj_Dict_TurnBattleType = 'TurnBattleType' # 本次攻击战斗类型:TurnBattleType_xxx
+Def_Obj_Dict_FightPetPlaceNum = 'FightPetPlaceNum' # 出战灵宠上阵位置,1~n
+Def_Obj_Dict_FightPetQuality = 'FightPetQuality' # 出战灵宠品质
 
 #---NPC字典-------
 #每道龙卷风最终坐标
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
index 671978d..8254e50 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
@@ -725,7 +725,7 @@
                         ("DWORD", "DataMapID", 1),
                         ("WORD", "LineID", 1),
                         ("DWORD", "NPCID", 0),
-                        ("DWORD", "SummerNPCID", 0),
+                        ("list", "PetNPCIDList", 0),
                         ("list", "AwardItemListFirst", 0),
                         ("list", "AwardItemList", 0),
                         ),
@@ -3709,7 +3709,7 @@
         self.DataMapID = 0
         self.LineID = 0
         self.NPCID = 0
-        self.SummerNPCID = 0
+        self.PetNPCIDList = []
         self.AwardItemListFirst = []
         self.AwardItemList = []
         return
@@ -3717,7 +3717,7 @@
     def GetDataMapID(self): return self.DataMapID # 数据地图ID
     def GetLineID(self): return self.LineID # 功能线路ID
     def GetNPCID(self): return self.NPCID # NPCID
-    def GetSummerNPCID(self): return self.SummerNPCID # 召唤兽ID
+    def GetPetNPCIDList(self): return self.PetNPCIDList # 灵宠NPCID列表
     def GetAwardItemListFirst(self): return self.AwardItemListFirst # 首次过关奖励列表[[物品ID,个数,是否拍品], ...]
     def GetAwardItemList(self): return self.AwardItemList # 再次过关奖励列表[[物品ID,个数,是否拍品], ...]
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AICommon.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AICommon.py
index 790207b..90d0128 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AICommon.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AICommon.py
@@ -306,10 +306,15 @@
         return False
 
     #CD时间
-    if tick - useSkill.GetLastUseTick() < useSkill.GetCoolDownTime():
-        #GameWorld.Log('检查CD时间触发失败 = %s'%(useSkill.GetSkillName()))
-        return False
-    
+    if curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightNum):
+        if useSkill.GetRemainTime():
+            #GameWorld.Log('技能回合CD中 = %s, %s'%(useSkill.GetSkillName(), useSkill.GetRemainTime()))
+            return False
+    else:
+        if tick - useSkill.GetLastUseTick() < useSkill.GetCoolDownTime():
+            #GameWorld.Log('检查CD时间触发失败 = %s'%(useSkill.GetSkillName()))
+            return False
+        
     curSkillUseTag = SkillShell.GetSkillAffectTag(useSkill)
     if curSkillUseTag == ChConfig.Def_UseSkillTag_CanAttackPlayer:
         if curTag.GetGameObjType() != IPY_GameWorld.gotPlayer:
@@ -360,11 +365,11 @@
     
     #---对宠物主人释放---
     if skillTag == ChConfig.Def_UseSkillTag_PetMaster:
-        if not PetControl.IsPet(curNPC):
+        if not PetControl.IsPetNPC(curNPC):
             GameWorld.ErrLog("该NPC非宠物,无法获得主人释放技能")
             return False
         
-        petOwner = PetControl.GetPetOwner(curNPC)
+        petOwner = PetControl.GetPetNPCOwner(curNPC)
         
         if petOwner == None:
             GameWorld.ErrLog("宠物(%s)对主人释放技能,找不到主人"%curNPC.GetRolePet().PetID)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
index 8105c44..44956ba 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
@@ -2310,7 +2310,7 @@
     #__NotifyMapPlayerSummonMapNPC(npcId, rebornX, rebornY)
     return curSummon
 
-def SummonNPC(gameObj, npcID, rebornX, rebornY):
+def SummonNPC(gameObj, npcID, rebornX, rebornY, skillIDList=None):
     ''' 某个实例进行召唤,有从属关系
     '''
     npcData = GameWorld.GetGameData().FindNPCDataByID(npcID)
@@ -2343,6 +2343,13 @@
     curSummon.SetSightLevel(gameObj.GetSightLevel())
     
     curSummon.Reborn(rebornX, rebornY, False)
+    #有指定的技能,重新学习
+    if skillIDList:
+        #GameWorld.DebugLog("指定召唤兽技能: npcID=%s,skillIDList=%s" % (npcID, skillIDList))
+        skillManger = curSummon.GetSkillManager()
+        skillManger.ResetSkill()
+        for skillID in skillIDList:
+            skillManger.LVUPSkillByID(skillID)
     NPCControl(curSummon).DoNPCRebornCommLogic(tick)
     #curSummon.RefreshView()
     return curSummon
@@ -3723,7 +3730,7 @@
         #范围校验
         if not posMap:
             GameWorld.ErrLog("__Func_GetRandPosInRefreshArea GetRefreshPosAt error: return None! npcID=%s" % curNPC.GetNPCID())
-            return
+            return curNPC.GetPosX(), curNPC.GetPosY()
         posMapX = posMap.GetPosX()
         posMapY = posMap.GetPosY()
         
@@ -4115,8 +4122,7 @@
 #        curNPC.ForbiddenSkillTypeList_Clear()
     
         #宠物特殊处理
-        if curNPC.GetGameNPCObjType() == IPY_GameWorld.gnotPet:
-            PetControl.RefurbishPetAttr(curNPC, canSyncClient)
+        if PetControl.RefurbishPetAttr(curNPC):
             return
         
         pvpPlayerID = curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_PVPPlayerID)
@@ -4137,7 +4143,7 @@
         return
     
     def SetPVPNPCPlayerAttr(self, pvpPlayerID):
-        PropDict = PlayerViewCacheTube.GetPlayerPropData(pvpPlayerID)
+        PropDict, PlusDict = PlayerViewCacheTube.GetPlayerPropPlusDictByID(pvpPlayerID)
         if not PropDict:
             return
         curNPC = self.__Instance
@@ -4160,9 +4166,18 @@
         GameObj.SetAtkBackDefRate(curNPC, PropDict.get("AtkBackDefRate", 0))
         GameObj.SetSuckHPPer(curNPC, PropDict.get("SuckHPPer", 0))
         GameObj.SetSuckHPDefPer(curNPC, PropDict.get("SuckHPDefPer", 0))
-        
-        GameWorld.DebugLog("SetPVPNPCPlayerAttr: objID=%s,NPCID=%s,pvpPlayerID=%s,maxHP=%s" 
-                           % (curNPC.GetID(), curNPC.GetNPCID(), pvpPlayerID, GameObj.GetMaxHP(curNPC)))
+                
+        skillManager = curNPC.GetSkillManager()
+        SkillInfo = PlusDict.get("SkillInfo", {})
+        for _, skillLVDict in SkillInfo.items():
+            for skillID, _ in skillLVDict.items():
+                skillManager.LearnSkillByID(skillID)
+                
+        GameWorld.DebugLog("SetPVPNPCPlayerAttr: objID=%s,NPCID=%s,pvpPlayerID=%s,maxHP=%s,SkillInfo=%s" 
+                           % (curNPC.GetID(), curNPC.GetNPCID(), pvpPlayerID, GameObj.GetMaxHP(curNPC), SkillInfo))
+        for index in range(0, skillManager.GetSkillCount()):
+            useSkill = skillManager.GetSkillByIndex(index)
+            GameWorld.DebugLog("    NPC SkillID=%s" % useSkill.GetSkillID())
         return
     
     def SetHelpBattleRobotRebornAttr(self, fightPower):
@@ -4603,9 +4618,9 @@
             return
         
         #宠物死亡调用独立接口
-        if curNPC_GameNPCObjType == IPY_GameWorld.gnotPet:
-            PetControl.SetPetDead(curNPC)
-            return
+        #if curNPC_GameNPCObjType == IPY_GameWorld.gnotPet:
+        #    PetControl.SetPetDead(curNPC)
+        #    return
         
         #---通用死亡逻辑---
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
index be4e983..58f8200 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
@@ -344,7 +344,7 @@
     if curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_ClientCustomScene):
         PlayerFB.DoExitCustomScene(curPlayer)
         
-    PetControl.DoLogic_PetLoadMapOK(curPlayer)
+    #PetControl.DoLogic_PetLoadMapOK(curPlayer)
     
     # 恢复视野,刷新自己的视野
     curPlayer.SetVisible(True)
@@ -566,7 +566,7 @@
         DataRecordPack.DR_PlayerLogin(curPlayer)
         EventReport.WriteEvent_login(curPlayer)
         #---玩家上线, 宠物逻辑处理---
-        PetControl.DoLogic_PetInfo_OnLogin(curPlayer, tick)
+        #PetControl.DoLogic_PetInfo_OnLogin(curPlayer, tick)
         
         PlayerFamily.FamilyPlayerOnLoginCross(curPlayer)
         
@@ -652,7 +652,7 @@
     PlayerRefineStove.DoOnLogin(curPlayer, tick)
     
     #---玩家上线, 宠物逻辑处理---
-    PetControl.DoLogic_PetInfo_OnLogin(curPlayer, tick)
+    #PetControl.DoLogic_PetInfo_OnLogin(curPlayer, tick)
     PlayerPet.OnPlayerPetLogin(curPlayer)
     
     #要在SetCanMove之后做这个事情, 否则OnEnter不会卡住玩家
@@ -1483,7 +1483,7 @@
     #===========================================================================
     
     #初始化宠物 通知客户端
-    PetControl.Sync_PetInfo_ChangeMap(curPlayer, tick)
+    #PetControl.Sync_PetInfo_ChangeMap(curPlayer, tick)
 
     #通知GameServer自己现在的地图
     curPlayer.Sync_GameServer_MapID()
@@ -1742,7 +1742,7 @@
     EventShell.EventResponse_OnMapEx(curPlayer)
     
     #触发切换地图宠物逻辑
-    PetControl.DoLogic_PetLoadMapOK(curPlayer)
+    #PetControl.DoLogic_PetLoadMapOK(curPlayer)
 
     #---检查是否卡障碍---
     curMap = GameWorld.GetMap()
@@ -2249,7 +2249,7 @@
         return
 
     #出战宠物同时移动
-    PetControl.FightPetFollowMove(curPlayer, sendPack_StartX, sendPack_StartY)
+    #PetControl.FightPetFollowMove(curPlayer, sendPack_StartX, sendPack_StartY)
     
     
     return
@@ -4510,7 +4510,7 @@
         #直接取db中配置的复活点
         PlayerControl.PlayerResetWorldPos(curPlayer, gameMap.GetRebornMapID(), gameMap.GetRebornMapX(), gameMap.GetRebornMapY(), False)
     #重新召唤宠物
-    PlayerPet.AutoSummonPet(curPlayer)
+    #PlayerPet.AutoSummonPet(curPlayer)
     
     #复活成功,重置状态
     PlayerControl.ChangePlayerAction(curPlayer, IPY_GameWorld.paNull)
@@ -6160,7 +6160,7 @@
         #出战宠物同时移动
         if moveType == 0 or tick - fightPet.GetActionTick() > 300:
     
-            PetControl.FightPetFollowMove(curPlayer,sendPack_SeverPosX, sendPack_SeverPosY)
+            #PetControl.FightPetFollowMove(curPlayer,sendPack_SeverPosX, sendPack_SeverPosY)
             fightPet.SetCurAction(IPY_GameWorld.laNPCNull)
             
     return True
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PetControl.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PetControl.py
index 371a3c2..08b1acd 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PetControl.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PetControl.py
@@ -22,150 +22,17 @@
 #IPY_RolePetMgr.PetList_Find\GetFightPet\PetList_At -> IPY_RolePet;
 #IPY_RolePet.GetRolePet -> tagIPY_RolePet(结构体); 
 #IPY_RolePet.GetPetData -> IPY_Pet;
-#
-#
 #---------------------------------------------------------------------
 import IPY_GameWorld
-import ChConfig
 import GameWorld
-import BuffSkill
-import SkillShell
 import PlayerPet
 import PlayerControl
 import NPCCommon
-import SkillCommon
-import GameMap
-import OperControlManager
-import ShareDefine
 
-import random
-import copy
-import math
 import GameObj
-import AICommon
-import BaseAttack
 import PassiveBuffEffMng
-import ChNetSendPack
 import IpyGameDataPY
-#---------------------------------------------------------------------
-
-DefPetRebornHPRate = 100  # 宠物死亡复活血量百分比
-
-#---------------------------------------------------------------------
-## 检查宠物是否出战中
-#  @param curPlayer 玩家对象
-#  @param curPetID 宠物ID
-#  @return True or False 是否出战中
-#  @remarks 检查宠物是否出战中
-def CheckPetIsFight(curPlayer, curPetID):
-    petMgr = curPlayer.GetPetMgr()
-    #出战的宠物对象
-    fightPetObj = petMgr.GetFightPet()  
-    
-    if fightPetObj == None:
-        return False
-    
-    if fightPetObj.GetRolePet().PetID == curPetID:
-        return True
-    
-    return False
-
-
-
-
-# 宠物跟随人物同时攻击
-def PetFight(curPlayer, tick):
-    
-    rolePet = curPlayer.GetPetMgr().GetFightPet()
-    #无出战宠物
-    if rolePet == None:
-        return
-    rolePetControl = NPCCommon.NPCControl(rolePet)
-    
-    useSkillTagID = curPlayer.GetUseSkillTagID()
-    useSkillTagType = curPlayer.GetUseSkillTagType()
-    
-    curTag = GameWorld.GetObj(useSkillTagID, useSkillTagType)
-    if not curTag or GameObj.GetHP(curTag) <= 0:
-        # 没有主目标就从仇恨中找
-        for i in range(curPlayer.GetAngryNPCCount()):
-            curTag = curPlayer.GetAngryNPCByIndex(i)
-            if not curTag or GameObj.GetHP(curTag) <= 0:
-                continue
-        
-    if not curTag or GameObj.GetHP(curTag) <= 0:
-        return
-    
-    #tagDist = GameWorld.GetDist(rolePet.GetPosX(), rolePet.GetPosY(), curTag.GetPosX(), curTag.GetPosY())
-    tagDist = 0
-    #---优先释放技能---
-    if AICommon.DoAutoUseSkill(rolePet, curTag, tagDist, tick):
-        return
-    
-    #---释放普通攻击--- 边走边攻击纠正一次位置
-    #if tagDist > rolePet.GetAtkDist():
-    #    rolePetControl.MoveToObj_Detel(curTag)
-    
-    #普通攻击
-    BaseAttack.Attack(rolePet, curTag, None, tick)
-    return
-
-
-#---------------------------------------------------------------------
-##出战宠物跟随移动
-# @param curPlayer 玩家实例
-# @param destPosX 移动目标X点
-# @param destPosY 移动目标Y点
-# @return 返回值无意义
-# @remarks 出战宠物跟随移动
-def FightPetFollowMove(curPlayer, destPosX, destPosY):
-    #主人在战斗中, 宠物不跟随移动
-
-    fightPet = curPlayer.GetPetMgr().GetFightPet()
-    #无出战宠物
-    if fightPet == None:
-        return
-
-    petControl = NPCCommon.NPCControl(fightPet)
-    movePosX, movePosY = petControl.GetMoveNearPos(destPosX, destPosY, 2)
-    # 执行一次重置位置,避免快速发包导致无法移动
-    fightPet.ChangePos(movePosX, movePosY)
-    
-    #movePosX, movePosY = petControl.GetMoveNearPos(destPosX, destPosY, 1)
-    PetMove(fightPet, movePosX, movePosY)
-    return
-
-
-def PetMove(fightPet, posX, posY):
-    
-    sendPack = ChNetSendPack.tagObjMove()
-    
-    sendPack.Clear()
-    sendPack.ObjID = fightPet.GetID()
-    sendPack.ObjType = IPY_GameWorld.gotNPC
-    sendPack.MoveType = IPY_GameWorld.mtNormal
-    sendPack.DestPosX = posX
-    sendPack.DestPosY = posY
-    sendPack.Speed = fightPet.GetSpeed()
-    sendPack.StartPosX = fightPet.GetPosX()
-    sendPack.StartPosY = fightPet.GetPosY()
-    
-    fightPet.NotifyAll(sendPack.GetBuffer(), sendPack.GetLength())
-    return
-#---------------------------------------------------------------------
-##清除出战宠物仇恨.
-# @param curPlayer 玩家实例
-# @return 返回值无意义
-# @remarks 清除出战宠物仇恨
-def ClearFightPetAngry(curPlayer):
-    fightPet = curPlayer.GetPetMgr().GetFightPet()
-    #无出战宠物
-    if fightPet == None:
-        return
-
-    petControl = NPCCommon.NPCControl(fightPet)
-    petControl.ClearNPCAngry()
-    return
+import ChConfig
 #---------------------------------------------------------------------
 
 ##获得宠物的拥有者
@@ -206,6 +73,44 @@
     
     return False
 #---------------------------------------------------------------------    
+
+def IsPetNPC(curObj):
+    ''' 是否灵宠NPC,召唤兽类型,玩家、玩家镜像、NPC通用,均可有灵宠召唤兽
+            区别于 IsPet 为旧版的 PY_GameWorld.gnotPet 实例对象,仅玩家实例有
+    '''
+    curObjType = curObj.GetGameObjType()
+    if curObjType != IPY_GameWorld.gotNPC:
+        return False
+    npcObjType = curObj.GetGameNPCObjType()
+    if npcObjType != IPY_GameWorld.gnotSummon:
+        return False
+    return curObj.GetType() == IPY_GameWorld.ntPet
+
+def GetPetNPCOwner(curPet):
+    ''' 获得宠物的拥有者,可能为玩家、玩家镜像、NPC
+        区别于 GetPetOwner 为旧版的 PY_GameWorld.gnotPet 归属者仅支持玩家
+    '''
+    if curPet == None:
+        return
+    
+    atkObjType = curPet.GetGameObjType()
+    if atkObjType != IPY_GameWorld.gotNPC:
+        return
+    
+    summonOwner = None
+    npcObjType = curPet.GetGameNPCObjType()
+    # 判断召唤兽主人
+    if npcObjType == IPY_GameWorld.gnotSummon:
+        curNPCDetail = GameWorld.GetObjDetail(curPet)
+        if curNPCDetail:
+            curNPCOwner = curNPCDetail.GetOwner()
+            if curNPCOwner:
+                summonOwner = GameWorld.GetObjDetail(curNPCOwner)
+                # 召唤兽主人为玩家
+                #if summonOwner and summonOwner.GetGameObjType() == IPY_GameWorld.gotPlayer:
+                #    return summonOwner, npcObjType
+    return summonOwner
+
 ##清空宠物身上所有buff
 # @param curPet 宠物对象
 # @return None
@@ -217,225 +122,92 @@
         buffState.Clear()
     
     return
-#---------------------------------------------------------------------    
-##召唤宠物
-# @param curPet 宠物对象
-# @param curPosX 宠物出身点位置
-# @param curPosY 宠物出身点位置
-# @return None
-# @remarks 召唤宠物,初始化宠物所有属性(C++处理)
-def SummonPet(curPet, curPosX, curPosY):
-    #出现(出战逻辑在C++, 将清空宠物的相关信息)
-    curPet.Summon(curPosX, curPosY)
-    #记录该宠物被召回
-    curPet.SetIsBattle(True)
-    #初始化宠物
-    InitRolePet(curPet)
-    PassiveBuffEffMng.GetPassiveEffManager().RegistPassiveEff(curPet)
-    return
+
 #---------------------------------------------------------------------
-##初始化宠物属性
-# @param rolePet 宠物实例
-# @param canSyncClient 是否通知客户端
-# @return 返回值无意义
-# @remarks 初始化宠物属性
-def InitRolePet(rolePet, canSyncClient=True):
-    #---初始化时钟---
-    NPCCommon.InitNPC(rolePet)
+def CalloutFightPet(curObj, petCacheInfo=None):
+    ## 出战上阵的灵宠
+    # @param petCacheInfo: 玩家灵宠功能缓存信息
+    # @return: [第1位置实例, 第2, ...]  按位置顺序,实例可能为None
     
-    #---初始化仇恨列表---
-    npcAngry = rolePet.GetNPCAngry()
-    if npcAngry.GetAngryCount() == 0:
-        npcAngry.Init(ChConfig.Def_Pet_Angry_Count)
-    else:
-        npcAngry.Clear()
+    fightPlaceCount = len(IpyGameDataPY.GetFuncEvalCfg("PetGoOutFight", 1)) # 战斗位置数
+    fightPetObjList = [None] * fightPlaceCount
+    if petCacheInfo == None and curObj.GetGameObjType() == IPY_GameWorld.gotPlayer:
+        petCacheInfo = PlayerPet.GetPetCacheInfo(curObj)
+    if not petCacheInfo: # 没有灵宠
+        return fightPetObjList
+    
+    fightPetDict = {} # 上阵的灵宠
+    for cacheInfo in petCacheInfo:
+        state = cacheInfo.get("state", 0)
+        if not state:
+            continue
+        fightPetDict[state] = cacheInfo
+        
+    if not fightPetDict: # 没有上阵的
+        return fightPetObjList
+    
+    posX, posY = curObj.GetPosX(), curObj.GetPosY()
+    for index in range(fightPlaceCount):
+        placeNum = index + 1
+        if placeNum not in fightPetDict:
+            continue
+        petInfo = fightPetDict[placeNum]
+        petNPCID = petInfo.get("npcID", 0)
+        if not petNPCID:
+            continue
+        quality = petInfo.get("quality", 0)
+        skillIDList = petInfo.get("skills", [])
+        petObj = NPCCommon.SummonNPC(curObj, petNPCID, posX, posY, skillIDList)
+        if not petObj:
+            continue
+        petObj.SetDict(ChConfig.Def_Obj_Dict_FightPetPlaceNum, placeNum)
+        petObj.SetDict(ChConfig.Def_Obj_Dict_FightPetQuality, quality)
+        fightPetObjList[index] = petObj
+    return fightPetObjList
 
-    #---初始化宠物属性---
-    petControl = NPCCommon.NPCControl(rolePet)
-    petControl.RefreshNPCState(canSyncClient)
-
-    #加一个防御措施, 如果宠物血为0, 不可召唤出来, 默认为宠物召回时候的血量
-    if GameObj.GetHP(rolePet) <= 0:
-        PetDead_ReStoreHP(rolePet, GameObj.GetMaxHP(rolePet))
-        GameWorld.ErrLog('宠物重生血量为0, PetID = %s' % (rolePet.GetRolePet().PetID))
-
-    return
-#---------------------------------------------------------------------
 ##召回出战的宠物
 # @param curPlayer 玩家实例
 # @return 布尔值
 # @remarks 召回出战的宠物
 def ReCallFightPet(curPlayer):
-    rolePetMgr = curPlayer.GetPetMgr()
-    #获得战斗的宠物
-    fightPet = rolePetMgr.GetFightPet()
-    
-    if fightPet == None:
-        return False
-    
-    #清空宠物身上附加的buff
-    ClearPetBuff(fightPet)
-    #记录该宠物被召回
-    fightPet.SetIsBattle(False)
-    #此死亡接口(C++将重置所有战斗属性, 要在刷一次)
-    NPCCommon.SetDeadEx(fightPet)
-    #刷新宠物属性
-    petControl = NPCCommon.NPCControl(fightPet)
-    petControl.RefreshNPCState(canSyncClient=True)
-    
-    #宠物数据物品状态变更
-    petItemIndex = PlayerPet.GetPetObjItemIndex(fightPet)
-    petItem = PlayerPet.GetPetDataItemByIndex(curPlayer, petItemIndex)
-    if petItem:
-        petItem.SetUserAttr(ShareDefine.Def_IudetPet_State, ShareDefine.Def_PetState_Null)
-    
-    petID = fightPet.GetRolePet().PetID
-    npcID = fightPet.GetRolePet().NPCID
-    rolePetMgr.PetList_SetFree(petID) # 永恒召回后默认放生, 最多只存在一个出战宠物实例
-    GameWorld.DebugLog("随从召回后默认放生, petID=%s, npcID=%s" % (petID, npcID))
     return True
-#---------------------------------------------------------------------
-## 设置宠物死亡
-#  @param curPet 宠物对象
-#  @return None
-#  @remarks 设置宠物死亡
-def SetPetDead(curPet):
-    #---执行死亡惩罚逻辑---
-    
-    #宠物主人
-    curPetOwner = GetPetOwner(curPet)
-    if not curPetOwner:
-        return
-    ReCallFightPet(curPetOwner)
-    return
 
-#---------------------------------------------------------------------
-##宠物死亡恢复血量
-# @param index 玩家索引
-# @param tick 时间戳
-# @return 返回值无意义
-# @remarks 宠物死亡恢复血量
-def PetDead_ReStoreHP(curPet, maxHP):
-    #设置宠物当前血量为其上限的30%
-    SetPetHP(curPet, int(maxHP * DefPetRebornHPRate / 100))
-    return
-#---------------------------------------------------------------------
-
-#---------------------------------------------------------------------
-##设置宠物HP
-# @param rolePet 玩家宠物
-# @param 值
-# @param canSyncClient是否通知客户端
-# @return 返回值无意义
-# @remarks 设置宠物HP
 def SetPetHP(rolePet, value, canSyncClient=True):
+    ##设置宠物HP
     GameObj.SetHP(rolePet, value, canSyncClient)
-    
     if not canSyncClient:
         return
-    
     #rolePet.Sync_RefreshProp(IPY_GameWorld.PetInfoRefresh_HP, value, True)
     return
-#---------------------------------------------------------------------
-##增加宠物HP
-# @param rolePet 玩家宠物
-# @param 值
-# @return 返回值无意义
-# @remarks 加宠物HP
+
 def AddPetHP(rolePet, value):
-    remainValue = min(GameObj.GetHP(rolePet) + value, GameObj.GetMaxHP(rolePet))
-    SetPetHP(rolePet, remainValue)
+    ##增加宠物HP
+    #remainValue = min(GameObj.GetHP(rolePet) + value, GameObj.GetMaxHP(rolePet))
+    #SetPetHP(rolePet, remainValue)
     return
 
-#---------------------------------------------------------------------
-##刷新宠物的属性
-# @param rolePet 玩家宠物
-# @param canSyncClient 是否通知客户端(默认为True)
-# @return 返回值无意义
-# @remarks 刷新宠物的属性
-def RefurbishPetAttr(rolePet, canSyncClient=True):
-    curPlayer = GetPetOwner(rolePet)
-    if not curPlayer:
+def RefurbishPetAttr(curPet):
+    ##刷新宠物的属性,仅支持召唤兽,废弃原 IPY_GameWorld.gnotPet
+    if not IsPetNPC(curPet):
+        return
+    petOwner = GetPetNPCOwner(curPet)
+    if not petOwner:
         return
     
-    petDataItem = PlayerPet.GetPetDataItem(curPlayer, rolePet)
-    if not petDataItem:
-        return
+    #宠物不可被攻击, 灵宠继承人物的 百分百攻击和攻速
+    curPet.SetMinAtk(petOwner.GetMinAtk())
+    curPet.SetMaxAtk(petOwner.GetMaxAtk())
+    GameObj.SetAtkSpeed(curPet, GameObj.GetAtkSpeed(petOwner))
     
-    # 变更需要同步的信息配置, 客户端取宠物信息全部以宠物物品数据为准
-    syncList = [
-                [lambda petObj:petObj.GetMaxAtk(), IPY_GameWorld.PetInfoRefresh_PhysicAtk], #攻击
-                #[lambda petObj:petObj.GetQualityLV(), IPY_GameWorld.PetInfoRefresh_QualityLV], #品质
-                ]
-    beforeValueList = []
-    for syncInfo in syncList:
-        beforeValueList.append(syncInfo[0](rolePet))
+    #allAttrList = [{} for i in range(4)]
+    ##计算技能对战斗属性的影响
+    #PlayerPet.CalcSkill_PetBattleEffect(curPlayer, rolePet, allAttrList)
+    ##计算Buff对战斗属性的影响
+    #PlayerPet.CalcBuffer_PetBattleEffect(rolePet, allAttrList)
     
-    #添加Buff到宠物身上
-    #AddPlayerPetSkillBuff(rolePet)
-    
-    #计算Buff对基础属性的影响
-    #SkillShell.CalcBuffer_NPCBaseEffect(rolePet)
-    
-    #宠物不可被攻击, 命中与攻速直接继承主人攻击属性,攻击取等级阶级加成
-    rolePet.SetMinAtk(PlayerControl.GetPetMinAtk(curPlayer))
-    rolePet.SetMaxAtk(PlayerControl.GetPetMaxAtk(curPlayer))
-    rolePet.SetSkillAtkRate(PlayerControl.GetPetSkillAtkRate(curPlayer))
-    GameObj.SetPetDamPer(rolePet, GameObj.GetPetDamPer(curPlayer))
-    #rolePet.SetMAtkMin(curPlayer.GetMAtkMin())
-    #rolePet.SetMAtkMax(curPlayer.GetMAtkMax())
-    rolePet.SetHit(curPlayer.GetHit())
-    rolePet.SetSpeed(curPlayer.GetSpeed())
-    
-    allAttrList = [{} for i in range(4)]
-    #计算技能对战斗属性的影响
-    PlayerPet.CalcSkill_PetBattleEffect(curPlayer, rolePet, allAttrList)
-    #计算Buff对战斗属性的影响
-    PlayerPet.CalcBuffer_PetBattleEffect(rolePet, allAttrList)
-    
-    GameWorld.DebugLog("RefurbishPetAttr ID=%s,npcID=%s,atk=%s,hit=%s,skillAtkRate=%s" 
-                       % (rolePet.GetID(), rolePet.GetNPCID(), rolePet.GetMaxAtk(), rolePet.GetHit(), rolePet.GetSkillAtkRate()))
-    
-    #是否需要通知客户端属性刷新
-    if not canSyncClient:
-        return
-    
-    #===========================================================================
-    # for i, syncInfo in enumerate(syncList):
-    #    nowValue = syncInfo[0](rolePet)
-    #    if beforeValueList[i] != nowValue:
-    #        rolePet.Sync_RefreshProp(syncInfo[1], nowValue, True)
-    #        #GameWorld.DebugLog("Sync_RefreshProp i=%s,befValue=%s,nowValue=%s,type=%s" % (i, beforeValueList[i], nowValue, syncInfo[1]))
-    #===========================================================================
+    GameWorld.DebugLog("RefurbishPetAttr ID=%s,npcID=%s,maxAtk=%s,atkSpeed=%s" 
+                       % (curPet.GetID(), curPet.GetNPCID(), curPet.GetMaxAtk(), curPet.GetMaxAtk()))
     return
-
-#---------------------------------------------------------------------
-##添加宠物技能Buff
-# @param rolePet 宠物实例
-# @return 返回值无意义
-# @remarks 添加宠物技能Buff
-#===============================================================================
-# def AddPlayerPetSkillBuff(rolePet):
-#    tick = GameWorld.GetGameWorld().GetTick()
-# 
-#    #---先清掉---
-#    passiveBuffManager = rolePet.GetPassiveBuf()
-#    passiveBuffManager.Clear()
-#    
-#    #---在加上---
-#    rolePetSkillManager = rolePet.GetSkillManager()
-#    
-#    for i in range(0, rolePetSkillManager.GetSkillCount()):
-#        learnSkill = rolePetSkillManager.GetSkillByIndex(i)
-#        #只添加被动技能
-#        if not SkillCommon.isPassiveBuffSkill(learnSkill):
-#            continue
-#        
-#        #加上buff不刷新状态
-#        BuffSkill.AddBuffNoRefreshState(rolePet, IPY_GameWorld.btPassiveBuf, learnSkill, tick)
-#    
-#    return
-#===============================================================================
 #---------------------------------------------------------------------
 def DoLogic_PlayerPetLearnSkillList(rolePet, learnSkillList):
     #GameWorld.DebugLog("DoLogic_PlayerPetLearnSkillList----%s"%learnSkillList)
@@ -454,144 +226,6 @@
     #rolePet.Sync_SkillList()     
     return
 
-#---------------------------------------------------------------------
-##检查当前指定宠物是否可出战
-# @param curPlayer 玩家实例
-# @param curPetObj 指定宠物实例
-# @return BOOL 是否可出战
-# @remarks 检查当前指定宠物是否可出战
-def CheckPetCanFight(curPlayer, curPetObj):
-    if not GetMapCanOutPet(curPlayer):
-        #Pet_liubo_314885 此地图禁止宠物
-        #PlayerControl.NotifyCode(curPlayer, "Pet_liubo_314885")
-        return False
-    
-    petData = curPetObj.GetPetData()
-    
-    #防沉迷
-    if curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_Wallow_LV) > ChConfig.Def_GameWallow_LV_First:
-        PlayerControl.NotifyCode(curPlayer, "AvoidSink09")
-        return False
-    
-    if GameObj.GetHP(curPlayer) <= 0:
-        #死亡状态无法出战宠物
-        return False
-    
-#    if curPlayer.GetLV() < petData.GetBringLV():
-#        #Pet_hgg_442426 对不起,您尚未到达该宠物的携带等级.
-#        PlayerControl.NotifyCode(curPlayer, "Pet_hgg_442426")
-#        return False
-#    
-#    if curPetObj.GetLV() - curPlayer.GetLV() > ChConfig.Def_PetRoleGapLVMax:
-#        #Pet_liubo_570355 对不起,您不能出战超过人物5级的宠物!
-#        PlayerControl.NotifyCode(curPlayer, "Pet_liubo_570355", [ChConfig.Def_PetRoleGapLVMax])
-#        return False
-    
-    #---是否已有宠物出战---
-    #fightPetObj = curPlayer.GetPetMgr().GetFightPet()  # 出战的宠物对象
-    
-    #if fightPetObj != None:
-    #    #Pet_hgg_892377 对不起,您只能同时出战一只宠物,请先召回出战的宠物!
-    #    PlayerControl.NotifyCode(curPlayer, "Pet_hgg_892377")
-    #    return False
-    
-    #GameWorld.Log('宠物血量:%s,宠物ID:%s'%(curPetStruct.HP,curPetStruct.PetID))
-    return True
-#---------------------------------------------------------------------
-
-#---------------------------------------------------------------------
-##玩家离开服务器通知宠物信息
-# @param curPlayer 玩家实例
-# @param tick 时间戳
-# @return 返回值无意义
-# @remarks 玩家离开服务器通知宠物信息
-def DoLogic_PetInfo_OnLeaveServer(curPlayer, tick):
-    return
-
-#---------------------------------------------------------------------
-##玩家登陆通知宠物信息
-# @param curPlayer 玩家实例
-# @param tick 时间戳
-# @return 返回值无意义
-# @remarks 玩家登陆通知宠物信息
-def DoLogic_PetInfo_OnLogin(curPlayer, tick):
-   
-    petMgr = curPlayer.GetPetMgr()
-    if GameWorld.IsCrossServer():
-        ''' 0435宠物出现包同步的ID是根据宠物的列表位置来的,比如在第2位发的就是2,而地图的可能是1,会导致ID不一致
-                            所以跨服服务器登录时先清除出战宠物列表,确保登录地图成功后都是从1开始的即可
-        '''
-        petList = []
-        for index in range(0, petMgr.PetList_Cnt()):
-            rolePet = petMgr.PetList_At(index)
-            petList.append(rolePet)
-        for rolePet in petList:
-            petMgr.PetList_SetFree(rolePet.GetRolePet().PetID)
-    else:
-        for index in range(0, petMgr.PetList_Cnt()):
-            rolePet = petMgr.PetList_At(index)
-            #刷新宠物信息并通知客户端
-            __RefreshAndSyncPetInfo(rolePet)
-            #自动出战宠物
-            __AutoSummonPet_OnLogin(curPlayer, rolePet)
-
-    return
-#---------------------------------------------------------------------
-##玩家上线, 自动出战宠物
-# @param curPlayer 玩家实例
-# @param tick 时间戳
-# @return 返回值无意义
-# @remarks 玩家上线, 自动出战宠物
-def __AutoSummonPet_OnLogin(curPlayer, rolePet):
-    #是否有出战标志
-    if not rolePet.GetIsBattle():
-        return
-    
-    if not GetMapCanOutPet(curPlayer):
-        #此地图禁止宠物
-        return
-    #召唤宠物出战
-    resultPos = GameMap.GetEmptyPlaceInArea(curPlayer.GetPosX(), curPlayer.GetPosY(), ChConfig.Def_SummonAppearDist)
-    
-    SummonPet(rolePet, resultPos.GetPosX(), resultPos.GetPosY())
-    return
-
-#---------------------------------------------------------------------
-##玩家切换地图通知宠物信息.
-# @param curPlayer 玩家实例
-# @param tick 时间戳
-# @return 返回值无意义
-# @remarks 玩家切换地图通知宠物信息.
-def Sync_PetInfo_ChangeMap(curPlayer, tick):
-    petMgr = curPlayer.GetPetMgr()
-    
-    for index in range(0, petMgr.PetList_Cnt()):
-        rolePet = petMgr.PetList_At(index)
-        #刷新宠物信息并通知客户端
-        __RefreshAndSyncPetInfo(rolePet)
-
-    return
-
-#---------------------------------------------------------------------
-##玩家携带宠物登陆地图
-# @param curPlayer 玩家实例
-# @return 返回值无意义
-# @remarks 
-def DoLogic_PetLoadMapOK(curPlayer):
-    
-    if GetMapCanOutPet(curPlayer):
-        #此地图宠物可以上
-        PlayerPet.AutoSummonPet(curPlayer)
-        return
-    
-    #此地图禁止宠物, 召回出战的宠物
-    if not ReCallFightPet(curPlayer):
-        return
-    
-    #Pet_liubo_314885 此地图禁止宠物
-    #PlayerControl.NotifyCode(curPlayer, "Pet_liubo_314885")
-    return
-
 def GetMapCanOutPet(curPlayer):
     ## 检查本地图可否出战宠物,支持前端自定义场景
     customMapID = PlayerControl.GetCustomMapID(curPlayer)
@@ -601,18 +235,4 @@
     else:
         canOutPet = GameWorld.GetMap().GetMapCanOutPet()
     return canOutPet
-
-#---------------------------------------------------------------------
-## 刷新宠物信息并通知客户端
-#  @param rolePet 宠物实例
-#  @return 无返回值
-#  @remarks 刷新宠物信息并通知客户端
-def __RefreshAndSyncPetInfo(rolePet):
-    #---刷新宠物信息(不通知客户端)---
-    InitRolePet(rolePet, False)
-
-    return
-
-#---------------------------------------------------------------------
-
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
index b218ef8..9d80ec4 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -1431,7 +1431,7 @@
 # @remarks 玩家离开服务器
 def PlayerLeaveServer(curPlayer, tick):
     #宠物下线逻辑, 这里要进行排行榜, 优先做, 避免玩家光环等属性影响宠物属性失效
-    PetControl.DoLogic_PetInfo_OnLeaveServer(curPlayer, tick)
+    #PetControl.DoLogic_PetInfo_OnLeaveServer(curPlayer, tick)
     
     #清除下线消失的buff, 在更新排行榜之前
     __DisconnectClearBuff(curPlayer, tick)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
index 43243ff..c6b3359 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
@@ -514,7 +514,7 @@
     NPCCommon.ClearPriWoodPile(curPlayer)
     GameWorld.Log("玩家开始自定义场景!mapID=%s,lineID=%s" % (mapID, lineID), playerID)
     if mapID:
-        PetControl.DoLogic_PetLoadMapOK(curPlayer)
+        #PetControl.DoLogic_PetLoadMapOK(curPlayer)
         FBLogic.OnEnterCustomScene(curPlayer, mapID, lineID)
         
     #默认回满血
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerPet.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerPet.py
index 8021f3a..9a5614c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerPet.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerPet.py
@@ -34,57 +34,36 @@
 import GameWorld
 import IpyGameDataPY
 import PlayerAttrFruit
-import GameMap
 import OpenServerCampaign
-import PlayerMagicWeapon
 import PlayerWeekParty
 import CalcNoLineEffect
-import PassiveBuffEffMng
-import CrossPlayerData
 import CalcLineEffect
 import PlayerActivity
 import ChPyNetSendPack
 import NetPackCommon
-import PlayerHorse
-import GameObj
 
 import random
 import math
-#---------------------------------------------------------------------
 
 
 def DoLogic_PetInfo_OnDay(curPlayer):
-            
     return
-#---------------------------------------------------------------------
 
-## 宠物功能开启
-#  @return: 是否激活成功
+def OnPlayerPetLogin(curPlayer):
+    ## 登录处理
+    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Pet):
+        return
+    
+    # 培养是后面加的功能,每次登录补检查一下功能开始时设置为培养1级
+    for trainType in xrange(1, GetPetTrainTypes() + 1):
+        if curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_PetTrainLV % trainType) == 0:
+            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_PetTrainLV % trainType, 1)
+            
+    Sync_PetTrainData(curPlayer)
+    return
+
 def DoPetOpen(curPlayer):
     GameWorld.DebugLog("DoPetOpen...", curPlayer.GetPlayerID())
-        
-#    firstPetData = ReadChConfig.GetEvalChConfig("FirstPet")
-#    petNPCID = firstPetData[0]
-#    isAutoFight = firstPetData[1]
-#    if petNPCID <= 0:
-#        return
-#    
-#    # 如果已经有该宠物, 不再给
-#    if GetPetDataItemIndexByNPCID(curPlayer, petNPCID) < 0:
-#        newPetItem = GetNewPetDataItem(curPlayer, petNPCID)
-#        if not newPetItem:
-#            return
-#        
-#        if not ItemControler.PlayerItemControler(curPlayer).PutInItem(ShareDefine.rptPet, newPetItem):
-#            return
-#        
-#        petData = newPetItem.GetUserData()
-#        DataRecordPack.DR_UsePetEgg(curPlayer, 0, petNPCID, petData)
-#        
-#    if isAutoFight and not curPlayer.GetPetMgr().GetFightPet():
-#        petItemIndex = GetPetDataItemIndexByNPCID(curPlayer, petNPCID)
-#        GameWorld.DebugLog("自动出战新手宠!petNPCID=%s,petItemIndex=%s" % (petNPCID, petItemIndex))
-#        DoChangePetState(curPlayer, petItemIndex, ShareDefine.Def_PetState_Fight)
     
     for trainType in xrange(1, GetPetTrainTypes() + 1):
         PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_PetTrainLV % trainType, 1)
@@ -116,62 +95,28 @@
         return
     
     newPetItem.SetUserAttr(ShareDefine.Def_IudetPet_NPCID, petNPCID)
-    newPetItem.SetUserAttr(ShareDefine.Def_IudetPet_State, ShareDefine.Def_PetState_Null)
+    newPetItem.SetUserAttr(ShareDefine.Def_IudetPet_State, 0)
     
     initClass = petIpyData.GetInitRank() if classlv == -1 else classlv#初始阶级
     newPetItem.SetUserAttr(ShareDefine.Def_IudetPet_ClassLV, max(0, initClass - 1)) #代码里从0开始
     newPetItem.SetUserAttr(ShareDefine.Def_IudetPet_QualityLV, petIpyData.GetQuality()) # 宠物品质
     
-    petSkillList = petIpyData.GetSkillID()
-    petSkillUnLockList = petIpyData.GetSkillUnLock()
-
-    for i, skillid in enumerate(petSkillList):
-        limitPetClassLV = petSkillUnLockList[i] # 学习此技能所需宠物阶级
-        if initClass < limitPetClassLV:
-            continue
-        curSkilData = GameWorld.GetGameData().GetSkillBySkillID(skillid)
-        if not curSkilData:
-            continue
-        if curSkilData.GetFuncType() == ChConfig.Def_SkillFuncType_PetOwnerSkill:
-            __GiveOwnerSkill(curPlayer, skillid)
-            continue
-        newPetItem.AddUserAttr(ShareDefine.Def_IudetPet_Skill, skillid)
-    
+    __UpdPetItemSkillByClass(curPlayer, newPetItem, False)    
     return newPetItem
-
-# 从称号获得技能
-def __GiveOwnerSkill(curPlayer, skillResID):
-    GameWorld.DebugLog("给灵宠主人技能: skillResID=%s" % skillResID)
-    skillData = GameWorld.GetGameData().FindSkillByType(skillResID, 1)
-    if skillData == None:
-        GameWorld.DebugLog("    not find skill(%s)" % skillResID)
-        return
-    if not SkillCommon.CheckSkillJob(curPlayer, skillData):
-        return
-    if not SkillShell.CheckLearnSkillCondition(curPlayer, skillData):
-        GameWorld.DebugLog("    learn skill condition isn't enough! skillResID=%s" % skillResID)
-        return
-    skillManager = curPlayer.GetSkillManager()
-    if skillManager.FindSkillBySkillTypeID(skillResID):
-        GameWorld.DebugLog("    have learned skill(%s)" % skillResID)
-        return
-    
-    skillManager.LVUpSkillBySkillTypeID(skillResID)
-    PassiveBuffEffMng.GetPassiveEffManager().RegistPassiveEff(curPlayer, skillResID)    
-    PlayerControl.PlayerControl(curPlayer).RefreshSkillFightPowerEx(skillResID, 0)
-    return
 
 ## 获取宠物实例对应的宠物数据物品
 def GetPetDataItem(curPetOwner, rolePet):
     if not curPetOwner:
         return
-    packItemIndex = GetPetObjItemIndex(rolePet)
+    packItemIndex = GetPetDataItemByNPCID(curPetOwner, rolePet.GetNPCID())
     return GetPetDataItemByIndex(curPetOwner, packItemIndex)
 
 ## 获取宠物数据物品
 def GetPetDataItemByIndex(curPlayer, petItemIndex):
     
     petDataPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptPet)
+    if petItemIndex < 0 or petItemIndex >= petDataPack.GetCount():
+        return
     petDataItem = petDataPack.GetAt(petItemIndex)
     if not ItemCommon.CheckItemCanUse(petDataItem):
         GameWorld.DebugLog("PetDataItem is none! PetItemIndex=%s" % petItemIndex, curPlayer.GetPlayerID())
@@ -190,10 +135,6 @@
             return petItem
     return
 
-## 永恒暂用友好度来存储该宠物所属的宠物背包物品索引
-def GetPetObjItemIndex(rolePet): return rolePet.GetRolePet().Friendliness
-def SetPetObjItemIndex(petStruct, petItemIndex): petStruct.Friendliness = petItemIndex
-
 ## 根据宠物NPCID获取宠物数据物品在背包中的索引
 def GetPetDataItemIndexByNPCID(curPlayer, petNPCID):
     petDataPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptPet)
@@ -206,8 +147,33 @@
         
     return -1
 
-#===================================================================================================
-# //////////////////////////////////////////////////////////////
+def GetPetItemCacheDict(petItem):
+    ## 单个灵宠物品缓存信息
+    npcID = petItem.GetUserAttr(ShareDefine.Def_IudetPet_NPCID)
+    state = petItem.GetUserAttr(ShareDefine.Def_IudetPet_State)
+    classLV = petItem.GetUserAttr(ShareDefine.Def_IudetPet_ClassLV)
+    quality = petItem.GetUserAttr(ShareDefine.Def_IudetPet_QualityLV)
+    star = petItem.GetUserAttr(ShareDefine.Def_IudetPet_Star)
+    skills = [petItem.GetUserAttrByIndex(ShareDefine.Def_IudetPet_Skill, i) for i in xrange(petItem.GetUserAttrCount(ShareDefine.Def_IudetPet_Skill))]
+    cacheDict = {"npcID":npcID}
+    state and cacheDict.update({"state":state})
+    classLV and cacheDict.update({"classLV":classLV})
+    quality and cacheDict.update({"quality":quality})
+    star and cacheDict.update({"star":star})
+    skills and cacheDict.update({"skills":skills})
+    return cacheDict
+
+def GetPetCacheInfo(curPlayer):
+    ## 获取灵宠功能缓存信息,暂时缓存全部,如果有需要改为仅缓存上阵的
+    itemCacheList = []
+    petDataPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptPet)
+    for petDataIndex in range(petDataPack.GetCount()):
+        petItem = petDataPack.GetAt(petDataIndex)
+        if petItem.IsEmpty():
+            continue
+        itemCacheList.append(GetPetItemCacheDict(petItem))
+    return itemCacheList
+
 # //16 03 宠物出战/召回#tagCPetStateChange
 # tagCPetStateChange       *   GettagCPetStateChange();
 # 
@@ -219,203 +185,77 @@
 #    // 状态.0-召回;1-出战;2xx-守护(守护+目标守护位)
 #    int      GetState(); 0~255
 # };
-#===================================================================================================
-##客户端封包响应
-#@param index 玩家索引
-#@param tick 时间戳
-#@return 返回值无意义
-#@remarks 客户端封包响应 //16 03 宠物出战/召回#tagCPetStateChange
 def PetStateChange(index, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
     pack = IPY_GameWorld.IPY_CPetStateChange()
     petItemIndex = pack.GetPetID()
     state = pack.GetState()
-    
-    #时间间隔未到,不处理(2010-06-23 16:20 策划刘鸿生说无须系统提示)
-    if tick - curPlayer.GetTickByType(ChConfig.TYPE_Player_Tick_ChangePetState) \
-       < ChConfig.TYPE_Player_Tick_Time[ChConfig.TYPE_Player_Tick_ChangePetState]:
-        #没有到刷新间隔
-        return
-    
-    curPlayer.SetTickByType(ChConfig.TYPE_Player_Tick_ChangePetState, tick)
-    
     DoChangePetState(curPlayer, petItemIndex, state)
     return
 
 ## 宠物战斗状态变更; 0-收回;1-出战  (手游版本只能出战)
-def DoChangePetState(curPlayer, petItemIndex, tagState, isRefresh=True):
-    if petItemIndex < 0:
-        return
+def DoChangePetState(curPlayer, petItemIndex, tagState):
+    # @param tagState: 0-收回;>=1-出战在第几个位置
     
-    if tagState not in ShareDefine.Def_PetStateList:
-        return
-        
     petItem = GetPetDataItemByIndex(curPlayer, petItemIndex)
     if not petItem:
         return
     
+    playerID = curPlayer.GetPlayerID()
+    petNPCID = petItem.GetUserAttr(ShareDefine.Def_IudetPet_NPCID)
+    curState = petItem.GetUserAttr(ShareDefine.Def_IudetPet_State)
+    GameWorld.DebugLog("宠物状态变更! petItemIndex=%s,petNPCID=%s,curState=%s,tagState=%s" 
+                       % (petItemIndex, petNPCID, curState, tagState), playerID)
+    # 收回
+    if tagState <= 0:
+        SetPetState(curPlayer, petItem, tagState)
+        return
+    
+    gooutFightRealmList = IpyGameDataPY.GetFuncEvalCfg("PetGoOutFight", 1)
+    if tagState > len(gooutFightRealmList):
+        GameWorld.DebugLog("    不存在该灵宠上阵位置! tagState=%s" % tagState, playerID)
+        return
+    needRealmLV = gooutFightRealmList[tagState - 1]
+    curRealmLV = curPlayer.GetOfficialRank()
+    if curRealmLV < needRealmLV:
+        GameWorld.DebugLog("    境界不足,无法上阵灵兽! curRealmLV=%s < %s, tagState=%s" % (curRealmLV, curRealmLV, tagState), playerID)
+        return
+    
     #判断是否达到可切换的阶数
     curClasslv = petItem.GetUserAttr(ShareDefine.Def_IudetPet_ClassLV)
-    petNPCID = petItem.GetUserAttr(ShareDefine.Def_IudetPet_NPCID)
     ipyData = GetPetIpydata(petNPCID)
     if not ipyData:
         return
     needClasslv = ipyData.GetUseNeedRank()
     if curClasslv < needClasslv - 1: #配置的阶级从1开始
-        GameWorld.DebugLog('    灵兽切换外观,阶级不足%s,不可切换!' % needClasslv)
+        GameWorld.DebugLog('    灵兽切换外观,阶级不足%s,不可切换!' % needClasslv, playerID)
         return
-    
-    curState = petItem.GetUserAttr(ShareDefine.Def_IudetPet_State) # 当前状态
- 
-    GameWorld.DebugLog("宠物状态变更!petItemIndex=%s,curState=%s,tagState=%s" 
-                       % (petItemIndex, curState, tagState))
-    
-    # 当前状态处理
-    if curState == ShareDefine.Def_PetState_Fight:
-#        curPet = curPlayer.GetPetMgr().GetFightPet()
-#        if curPet:
-#            #已是出战状态, C++召唤坐标和人重叠
-#            resultPos = GameMap.GetEmptyPlaceInArea(curPlayer.GetPosX(), curPlayer.GetPosY(), ChConfig.Def_SummonAppearDist)
-#            curPet.ResetPos(resultPos.GetPosX(), resultPos.GetPosY())
-#            PassiveBuffEffMng.GetPassiveEffManager().RegistPassiveEff(curPet)
-#            PassiveBuffEffMng.GetPassiveEffManager().RegistPassiveBuff(curPet)
-#        return
-        #18/10/15 因为某种未知原因宠物物品的状态是出战(实际场景中未出战),导致该宠物无法出战,故再次发包出战时,此处不拦!
-        PetControl.ReCallFightPet(curPlayer)
-   
+    SetPetState(curPlayer, petItem, tagState)
+    return
+
+def SetPetState(curPlayer, petItem, tagState):
+    ## 设置灵宠出战状态
+    # @param tagState: 0-收回;>=1-出战在第几个位置
+    npcID = petItem.GetUserAttr(ShareDefine.Def_IudetPet_NPCID)
+    if tagState <= 0:
+        GameWorld.DebugLog("    灵宠下阵: npcID=%s" % npcID, curPlayer.GetPlayerID())
     else:
-        pass
-    
-    # 目标状态处理
-    if tagState == ShareDefine.Def_PetState_Null:
-        #这里可不再宠物设置Null状态
-        #petItem.SetUserAttr(ShareDefine.Def_IudetPet_State, ShareDefine.Def_PetState_Null)
-        pass
-    elif tagState == ShareDefine.Def_PetState_Fight:
-        __DoPetGoOutToFight(curPlayer, petItem)
-   
-        
-    if isRefresh:
-        RefreshPetItemAddAttr(curPlayer, True) # 不刷排行榜
-        
-#    if petItem.GetUserAttr(ShareDefine.Def_IudetPet_State) != tagState:
-#        petItem.SetUserAttr(ShareDefine.Def_IudetPet_State, tagState)
-#        GameWorld.DebugLog("切换宠物状态异常防范! curState=%s,tagState=%s" % (curState, tagState))
-        
-    if not GameWorld.IsCrossServer():
-        dataList = [petNPCID, curClasslv, tagState]
-        CrossPlayerData.SendDataToCrossServer(curPlayer, CrossPlayerData.CrossData_PetState, dataList)
-        
+        petDataPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptPet)
+        for index in range(petDataPack.GetCount()):
+            item = petDataPack.GetAt(index)
+            if item.IsEmpty():
+                continue
+            if item.GetUserAttr(ShareDefine.Def_IudetPet_State) == tagState:
+                SetPetState(curPlayer, item, 0)
+        GameWorld.DebugLog("    灵宠上阵: npcID=%s,tagState=%s" % (npcID, tagState), curPlayer.GetPlayerID())
+    petItem.SetUserAttr(ShareDefine.Def_IudetPet_State, tagState)
     return
 
 def CrossServer_DoChangePetState(curPlayer, dataList):
     ## 跨服处理 宠物战斗状态变更
-    petNPCID, curClasslv, tagState = dataList
-    petItem = GetPetDataItemByNPCID(curPlayer, petNPCID)
-    if not petItem:
-        newPetItem = GetNewPetDataItem(curPlayer, petNPCID)
-        if not newPetItem:
-            return
-        if not ItemControler.PlayerItemControler(curPlayer).PutInItem(ShareDefine.rptPet, newPetItem):
-            return
-        petItem = GetPetDataItemByNPCID(curPlayer, petNPCID)
-    if not petItem:
-        return
-    
-    curItemClasslv = petItem.GetUserAttr(ShareDefine.Def_IudetPet_ClassLV)
-    # 处理技能问题,暂不处理
-    if curClasslv > curItemClasslv:
-        pass
-    
-    curState = petItem.GetUserAttr(ShareDefine.Def_IudetPet_State) # 当前状态
-    if curState == ShareDefine.Def_PetState_Fight:
-        PetControl.ReCallFightPet(curPlayer)
-        
-    if tagState == ShareDefine.Def_PetState_Fight:
-        __DoPetGoOutToFight(curPlayer, petItem)
-        
     return
 
-
-## 执行宠物出战逻辑
-def __DoPetGoOutToFight(curPlayer, petItem):
-    npcID = petItem.GetUserAttr(ShareDefine.Def_IudetPet_NPCID)
-
-    petNpcData = GameWorld.GetGameData().FindNPCDataByID(npcID)
-    if not petNpcData:
-        GameWorld.Log("PetStateChange FindNPCDataByID, pet npcID = %s" % (npcID))
-        return
-    
-    petMgr = curPlayer.GetPetMgr()
-    rolePet = petMgr.PetList_Add(npcID)
-    if rolePet == None:
-        GameWorld.ErrLog("PetStateChange PetList_Add, 添加宠物 = %s失败" % (npcID))
-        return
-    
-    #---初始化宠物属性---
-    petStruct = rolePet.GetRolePet()
-    petID = petStruct.PetID
-    petStruct.BindType = petItem.GetIsBind()
-    petStruct.Name = str(petStruct.PetID)#petNpcData.GetName() 配表不是UTF8会导致报错,默认用ID当名字
-    petStruct.DailyTrainCnt = PlayerHorse.GetHorsePetSkinIndex(curPlayer, 2, npcID)
-    # 宠物lv 改为 阶级 用于客户端显示名字颜色用
-#    classLV = petItem.GetUserAttr(ShareDefine.Def_IudetPet_ClassLV)
-#    rolePet.SetLV(classLV)
-
-    #位置.1, 在宠物列表; 2, 在物品背包     
-    petStruct.Pos = 1
-    
-    petItemIndex = petItem.GetItemPlaceIndex()
-    SetPetObjItemIndex(petStruct, petItemIndex)
-    rolePet.SetRolePet(petStruct)
-    
-    learnSkillList, passiveSkillList = GetPetLearnSkill(curPlayer)
-    PetControl.DoLogic_PlayerPetLearnSkillList(rolePet, learnSkillList)
-    
-    #---刷新属性(不通知)---
-    #GameWorld.DebugLog("刷前: petID=%s,playerID=%s,npcID=%s,BindType=%s,AIMode=%s,PetIndex=%s,grade=%s,qualLV=%s," 
-    #                   % (petStruct.PetID, petStruct.PlayerID, petStruct.NPCID, petStruct.BindType, petStruct.AIMode, petStruct.PetIndex,
-    #                      rolePet.GetGrade(), rolePet.GetQualityLV()))
-    petControl = NPCCommon.NPCControl(rolePet)
-    petControl.RefreshNPCState(canSyncClient=False)
-    #GameWorld.DebugLog("刷后: petID=%s,playerID=%s,npcID=%s,BindType=%s,AIMode=%s,PetIndex=%s,grade=%s,qualLV=%s," 
-    #                   % (petStruct.PetID, petStruct.PlayerID, petStruct.NPCID, petStruct.BindType, petStruct.AIMode, petStruct.PetIndex,
-    #                      rolePet.GetGrade(), rolePet.GetQualityLV()))
-    
-    #当前血量(不通知)
-    PetControl.SetPetHP(rolePet, GameObj.GetMaxHP(rolePet), False)
-    
-    #---通知客户端---
-    #rolePet.Sync_PetInfo()
-    #刷新技能栏
-    #rolePet.Sync_SkillList()
-    
-    #---收到"宠物出战"请求---
-    #检查是否可出战
-    if not PetControl.CheckPetCanFight(curPlayer, rolePet):
-        GameWorld.DebugLog("不可出战!PetList_SetFree petID=%s" % petID)
-        petMgr.PetList_SetFree(petID)
-        return
-
-    # 先招回出战中的宠物
-    PetControl.ReCallFightPet(curPlayer)
-    
-    #召唤宠物出战
-    resultPos = GameMap.GetEmptyPlaceInArea(curPlayer.GetPosX(), curPlayer.GetPosY(), ChConfig.Def_SummonAppearDist)
-    PetControl.SummonPet(rolePet, resultPos.GetPosX(), resultPos.GetPosY())
-    petItem.SetUserAttr(ShareDefine.Def_IudetPet_State, ShareDefine.Def_PetState_Fight)
-    
-    #记录出战的宠物索引 默认+1 0则代表没有
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FightPetIndex, petItemIndex + 1)
-    rolePet.SetSightLevel(curPlayer.GetSightLevel())
-    return True
-
 def AutoSummonPet(curPlayer):
-    #重新召唤之前的宠物,复活、切换地图时调用
-    fightPetIndex = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FightPetIndex)
-    if not fightPetIndex:
-        return
-    DoChangePetState(curPlayer, fightPetIndex - 1, ShareDefine.Def_PetState_Fight)
     return
 
 ## 获取出战宠物要学的技能
@@ -508,16 +348,7 @@
     EventShell.EventRespons_OnActivatePet(curPlayer, petNPCID)
     sysMark = ipyData.GetUnlockSys() or 'GetPet'
     PlayerControl.WorldNotify(0, sysMark, [curPlayer.GetName(), petNPCID])
-    rolePet = curPlayer.GetPetMgr().GetFightPet()
-    if not rolePet:
-        petItemIndex = GetPetDataItemIndexByNPCID(curPlayer, petNPCID)
-        GameWorld.DebugLog("没有宠物出战,获得新宠物,默认出战该宠物!petNPCID=%s,petItemIndex=%s" % (petNPCID, petItemIndex))
-        DoChangePetState(curPlayer, petItemIndex, ShareDefine.Def_PetState_Fight)
-    else:
-        
-        if rolePet:
-            learnSkillList, passiveSkillList = GetPetLearnSkill(curPlayer)
-            PetControl.DoLogic_PlayerPetLearnSkillList(rolePet, learnSkillList)
+    
     RefreshPetItemAddAttr(curPlayer, True)
     
     # 开服活动数据
@@ -550,6 +381,7 @@
                 packItem.SetUserAttr(ShareDefine.Def_IudetPet_ClassLV, max(0, min(classlv, maxClassLV) - 1))
             packItem.SetUserAttr(ShareDefine.Def_IudetPet_QualityLV, quality) # 宠物品质
             GameWorld.DebugLog("已经拥有该宠物! i=%s,petItemNPCID=%s,petNPCID=%s" % (i, petItemNPCID, petNPCID))
+            __UpdPetItemSkillByClass(curPlayer, packItem, True)
             return True
         
     if classlv == None:
@@ -564,25 +396,8 @@
         return
     if not refresh:
         return True
-    petItemIndex = GetPetDataItemIndexByNPCID(curPlayer, petNPCID)
-    DoChangePetState(curPlayer, petItemIndex, ShareDefine.Def_PetState_Fight)
     RefreshPetItemAddAttr(curPlayer, True)
     return True
-#===============================================================================
-
-
-## 获取已激活的灵兽ID
-def GetActivePetID(curPlayer):
-    petIDList = []
-    petPackIndex = ShareDefine.rptPet
-    petPack = curPlayer.GetItemManager().GetPack(petPackIndex)
-    for i in range(petPack.GetCount()):
-        packItem = petPack.GetAt(i)
-        if packItem.IsEmpty():
-            continue
-        petItemNPCID = packItem.GetUserAttr(ShareDefine.Def_IudetPet_NPCID)
-        petIDList.append(petItemNPCID)
-    return petIDList
 
 #// A7 04 宠物升阶 #tagCMPetClassUP
 #
@@ -676,35 +491,22 @@
     
     if lackCnt > 0:
         return
-        #===========================================================================================
-        # if not isAutoBuy:
-        #    return
-        # lackCost = ItemCommon.GetAutoBuyItemNeedGold({autoBuyItemID:lackCnt})
-        # if lackCost <= 0:
-        #    return
-        # itemData = GameWorld.GetGameData().GetItemByTypeID(autoBuyItemID)
-        # itemName = autoBuyItemID if not itemData else itemData.GetName()
-        # infoDict = {ChConfig.Def_Cost_Reason_SonKey:itemName}
-        # if not PlayerControl.PayMoney(curPlayer, IPY_GameWorld.TYPE_Price_Gold_Money, lackCost,
-        #                              ChConfig.Def_Cost_PetClassUP, infoDict, lackCnt):
-        #    return
-        #===========================================================================================
-       
+    
     playerName = curPlayer.GetName()
    
     curEff = curItem.GetEffectByIndex(0)
     addExp = curEff.GetEffectValue(0) * costItemCount
     updExp = curPetClassExp + addExp
     updClassLV = classLV
-    for lv in xrange(classLV, maxClassLV-1):
-        ipyData = IpyGameDataPY.GetIpyGameData("PetClassCost", petNPCID, lv+1)
+    for lv in xrange(classLV, maxClassLV - 1):
+        ipyData = IpyGameDataPY.GetIpyGameData("PetClassCost", petNPCID, lv + 1)
         if not ipyData:
             break
         upNeedExp = ipyData.GetUpNeedExp()
         if updExp < upNeedExp:
             break
         updExp -= upNeedExp
-        updClassLV +=1
+        updClassLV += 1
         
     
     #扣除物品
@@ -713,42 +515,14 @@
         ItemCommon.ReduceItem(curPlayer, curItemPack, itemIndexList, delCnt, True, ChConfig.ItemDel_Pet)
         
     # 更新经验值
-    if updClassLV+1 >=maxClassLV:
-        updExp =0
+    if updClassLV + 1 >= maxClassLV:
+        updExp = 0
     petDataItem.SetUserAttr(ShareDefine.Def_IudetPet_Exp, updExp)
     if updClassLV > classLV:
         petDataItem.SetUserAttr(ShareDefine.Def_IudetPet_ClassLV, updClassLV)
         
-        # 升阶开启技能
-        petIpyData = GetPetIpydata(petNPCID)
-        petSkillList = petIpyData.GetSkillID()
-        petSkillUnLockList = petIpyData.GetSkillUnLock()
-        sysMarkList = petIpyData.GetSkillUnLockSys()
-        learnSkillList = []
-        for i, skillid in enumerate(petSkillList):
-            limitPetClassLV = petSkillUnLockList[i] # 学习此技能所需宠物阶级
-            #上一阶已经处理过的技能不再处理
-            if updClassLV >= limitPetClassLV:
-                continue
-            # 未达到所需阶级
-            if updClassLV + 1 < limitPetClassLV:
-                continue
-            curSkilData = GameWorld.GetGameData().GetSkillBySkillID(skillid)
-            if not curSkilData:
-                continue
-            if curSkilData.GetFuncType() == ChConfig.Def_SkillFuncType_PetOwnerSkill:
-                __GiveOwnerSkill(curPlayer, skillid)
-                continue
-            petDataItem.AddUserAttr(ShareDefine.Def_IudetPet_Skill, skillid)
-            if not SkillCommon.isPassiveAttr(curSkilData):
-                #被动技能不学
-                learnSkillList.append(skillid)
-            #广播
-            sysMark = sysMarkList[i] if i < len(sysMarkList) else 'PetUpLv'
-            PlayerControl.WorldNotify(0, sysMark, [playerName, petNPCID, limitPetClassLV, skillid])
-            #增加升级活跃点效果
-            PlayerActivity.AddActivityByLVOnLearnSkill(curPlayer, skillid)
-            
+        learnSkillList = __UpdPetItemSkillByClass(curPlayer, petDataItem, True)
+        
         if not learnSkillList and updClassLV + 1 == maxClassLV:
             PlayerControl.WorldNotify(0, 'PetUpLvMax', [playerName, petNPCID])
         # 如果是当前出战的宠物, 则该宠物学习技能
@@ -769,6 +543,65 @@
     #EventReport.WriteEvent_pet_class(curPlayer, petNpcData.GetName(), classLV, petClassExp, updClassLV, newClassExp)
     
     return
+
+def __UpdPetItemSkillByClass(curPlayer, petDataItem, isNotify=False):
+    # 升阶更新灵宠技能,支持同skillTypeID技能升级
+    playerName = curPlayer.GetName()
+    petNPCID = petDataItem.GetUserAttr(ShareDefine.Def_IudetPet_NPCID)
+    classLV = petDataItem.GetUserAttr(ShareDefine.Def_IudetPet_ClassLV) + 1
+    alreadyLearnSkillInfo = {}
+    for i in xrange(petDataItem.GetUserAttrCount(ShareDefine.Def_IudetPet_Skill)):
+        skillID = petDataItem.GetUserAttrByIndex(ShareDefine.Def_IudetPet_Skill, i)
+        skillData = GameWorld.GetGameData().GetSkillBySkillID(skillID)
+        if not skillData:
+            continue
+        skillTypeID = skillData.GetSkillTypeID()
+        alreadyLearnSkillInfo[skillTypeID] = [skillID, i]
+    #GameWorld.DebugLog("开始更新灵宠技能: petNPCID=%s,classLV=%s,alreadyLearnSkillInfo=%s" % (petNPCID, classLV, alreadyLearnSkillInfo))
+    
+    petIpyData = GetPetIpydata(petNPCID)
+    petSkillList = petIpyData.GetSkillID()
+    petSkillUnLockList = petIpyData.GetSkillUnLock()
+    sysMarkList = petIpyData.GetSkillUnLockSys()
+    learnSkillList = []
+    for i, skillID in enumerate(petSkillList):
+        limitPetClassLV = petSkillUnLockList[i] # 学习此技能所需宠物阶级
+        if classLV < limitPetClassLV:
+            #GameWorld.DebugLog("    未满足学习阶级: i=%s,skillID=%s,limitPetClassLV=%s" % (i, skillID, limitPetClassLV))
+            break
+        skillData = GameWorld.GetGameData().GetSkillBySkillID(skillID)
+        if not skillData:
+            continue
+        skillTypeID = skillData.GetSkillTypeID()
+        alreadyLearnSkillID, skillIndex = alreadyLearnSkillInfo.get(skillTypeID, [0, -1])
+        if skillID <= alreadyLearnSkillID:
+            #GameWorld.DebugLog("    技能已经学习过: i=%s,skillID=%s <= alreadyLearnSkillID=%s" % (i, skillID, alreadyLearnSkillID))
+            continue
+        if skillData.GetFuncType() == ChConfig.Def_SkillFuncType_PetOwnerSkill:
+            #GameWorld.DebugLog("    主人学习技能: i=%s,skillID=%s" % (i, skillID))
+            SkillCommon.GivePlayerSkillByJobSkill(curPlayer, [skillID])
+            continue
+        if not alreadyLearnSkillID:
+            #GameWorld.DebugLog("    学习新的技能: i=%s,skillID=%s" % (i, skillID))
+            petDataItem.AddUserAttr(ShareDefine.Def_IudetPet_Skill, skillID)
+        else:
+            #GameWorld.DebugLog("    学习升级技能: i=%s,skillID=%s,skillIndex=%s" % (i, skillID, skillIndex))
+            petDataItem.UpdataUserAttrByIndex(ShareDefine.Def_IudetPet_Skill, skillIndex, skillID)
+        if not SkillCommon.isPassiveAttr(skillData):
+            #被动技能不学
+            learnSkillList.append(skillID)
+        #广播
+        sysMark = sysMarkList[i] if i < len(sysMarkList) else 'PetUpLv'
+        if isNotify and sysMark:
+            PlayerControl.WorldNotify(0, sysMark, [playerName, petNPCID, limitPetClassLV, skillID])
+        #增加升级活跃点效果
+        PlayerActivity.AddActivityByLVOnLearnSkill(curPlayer, skillID)
+        
+    nowSkillIDList = []
+    for i in xrange(petDataItem.GetUserAttrCount(ShareDefine.Def_IudetPet_Skill)):
+        nowSkillIDList.append(petDataItem.GetUserAttrByIndex(ShareDefine.Def_IudetPet_Skill, i))
+    GameWorld.DebugLog("灵宠最新技能: nowSkillIDList=%s" % nowSkillIDList)
+    return learnSkillList
 
 def GetTotalPetLV(curPlayer):
     totalPetLV = 0
@@ -801,18 +634,6 @@
         totalPetCount += 1
     return totalPetCount
 
-def IsPetMaxLV(curPlayer, petNPCID):
-    petItem = GetPetDataItemByNPCID(curPlayer, petNPCID)
-    if not petItem:
-        return
-    petNPCID = petItem.GetUserAttr(ShareDefine.Def_IudetPet_NPCID)
-    classLV = petItem.GetUserAttr(ShareDefine.Def_IudetPet_ClassLV)
-    petIpyData = GetPetIpydata(petNPCID)
-    if not petIpyData:
-        return
-    maxClassLV = petIpyData.GetMaxRank()
-    return classLV + 2 > maxClassLV
-
 ## 刷新宠物数据物品增加的属性
 def RefreshPetItemAddAttr(curPlayer, isUpdBillboard):
     CalcPetItemAddPlayerAttr(curPlayer)
@@ -823,9 +644,9 @@
     else:
         PlayerControl.PlayerControl(curPlayer).RefreshPlayerAttrState(isForce=True)
         
-    fightPet = curPlayer.GetPetMgr().GetFightPet()
-    if fightPet:
-        PetControl.RefurbishPetAttr(fightPet)
+    #fightPet = curPlayer.GetPetMgr().GetFightPet()
+    #if fightPet:
+    #    PetControl.RefurbishPetAttr(fightPet)
     return
 
 def CalcSkill_PetBattleEffect(curPlayer, rolePet, allAttrListPet):
@@ -968,7 +789,7 @@
         fpExTotal += petIpyData.GetInitFightPower() # 初始战力
         #觉醒战力
         skinData = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorsePetSkinData % (2, petItemNPCID), 0)
-        skinIpyData = IpyGameDataPY.GetIpyGameDataNotLog('HorsePetSkin', 2, petItemNPCID, skinData/100)
+        skinIpyData = IpyGameDataPY.GetIpyGameDataNotLog('HorsePetSkin', 2, petItemNPCID, skinData / 100)
         if skinIpyData:
             for attrID, attrValue in skinIpyData.GetAttrInfo().items():
                 PlayerControl.CalcAttrDict_Type(attrID, attrValue, allAttrListPetSkin)
@@ -1020,19 +841,6 @@
     totalMinAtk = classAddAtk
     totalMaxAtk = classAddAtk
     return totalMinAtk, totalMaxAtk, qualityAttrInfo
-
-def OnPlayerPetLogin(curPlayer):
-    ## 登录处理
-    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Pet):
-        return
-    
-    # 培养是后面加的功能,每次登录补检查一下功能开始时设置为培养1级
-    for trainType in xrange(1, GetPetTrainTypes() + 1):
-        if curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_PetTrainLV % trainType) == 0:
-            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_PetTrainLV % trainType, 1)
-            
-    Sync_PetTrainData(curPlayer)
-    return
 
 def GetPetTrainTypes():
     return len(IpyGameDataPY.GetFuncEvalCfg("PetUpItem", 3))
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py
index c66182b..bb61405 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py
@@ -141,7 +141,7 @@
         
     if useSkillData and useSkillData.GetSkillID() != ChConfig.Def_SkillID_Somersault:
         # 跟随玩家同频率攻击
-        PetControl.PetFight(curPlayer, tick)
+        #PetControl.PetFight(curPlayer, tick)
         SummonFollowAtk(curPlayer, tick)
     #===========================================================================
     # if not result:
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py
index fa6ba4e..4196229 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py
@@ -35,6 +35,7 @@
 import PyGameData
 import PlayerTJG
 import SkillShell
+import PlayerPet
 import GameObj
 
 import time
@@ -161,6 +162,8 @@
     return json.dumps(classItemDataDict, ensure_ascii=False).replace(" ", "")
 
 def UpdPlayerPropPlusCache(curPlayer):
+    if not curPlayer:
+        return
     curPlayerPropDict = {}
     curPlayerPropDict["AccID"] = curPlayer.GetAccID()
     curPlayerPropDict["LV"] = curPlayer.GetLV()
@@ -247,9 +250,12 @@
     curPlayerPlusDict["TotalStoneLV"] = Operate_EquipStone.GetTotalStoneLV(curPlayer)
     curPlayerPlusDict["TotalEquipWashLV"] = Operate_EquipWash.GetTotalEquipWashLV(curPlayer)
     #主动技能总等级
-    curPlayerPlusDict["TotalSkillLV"] = SkillShell.GetAllSkillLV(curPlayer, ChConfig.Def_SkillFuncType_FbSkill)
+    skillInfo = SkillShell.GetAllSkillInfo(curPlayer, [ChConfig.Def_SkillFuncType_FbSkill, ChConfig.Def_SkillFuncType_NormalAttack])
+    curPlayerPlusDict["SkillInfo"] = skillInfo
+    curPlayerPlusDict["TotalSkillLV"] = sum(skillInfo.get(ChConfig.Def_SkillFuncType_FbSkill, {}).values())
+    
     #灵宠数据
-    curPlayerPlusDict["Pet"] = __GetPetInfo(curPlayer)
+    curPlayerPlusDict["Pet"] = PlayerPet.GetPetCacheInfo(curPlayer)
     
     #坐骑数据
     curPlayerPlusDict["Horse"] = __GetHorseInfo(curPlayer)
@@ -273,16 +279,23 @@
 def GetPlayerPropPlusCache(curPlayer):
     #玩家属性缓存
     UpdPlayerPropPlusCache(curPlayer)
-    return GetPlayerPropPlusCacheByID(curPlayer.GetPlayerID())
+    return GetPlayerPropPlusCacheByID(curPlayer.GetPlayerID(), False)
 
-def GetPlayerPropPlusCacheByID(playerID):
+def GetPlayerPropPlusCacheByID(playerID, checkUpd=False):
     #玩家属性缓存
-    viewCache = PyGameData.g_playerViewCache.get(playerID, {})
-    curPlayerPropDict = viewCache.get("PropData", {})
-    curPlayerPlusDict = viewCache.get("PlusData", {})
-    PropData = json.dumps(curPlayerPropDict, ensure_ascii=False).replace(" ", "")
-    PlusData = json.dumps(curPlayerPlusDict, ensure_ascii=False).replace(" ", "")
+    PropDict, PlusDict = GetPlayerPropPlusDictByID(playerID, checkUpd)
+    PropData = json.dumps(PropDict, ensure_ascii=False).replace(" ", "")
+    PlusData = json.dumps(PlusDict, ensure_ascii=False).replace(" ", "")
     return PropData, PlusData
+
+def GetPlayerPropPlusDictByID(playerID, checkUpd=False):
+    #玩家属性字典
+    if checkUpd:
+        UpdPlayerPropPlusCache(GameWorld.GetPlayerManager().FindPlayerByID(playerID))
+    viewCache = PyGameData.g_playerViewCache.get(playerID, {})
+    PropDict = viewCache.get("PropData", {})
+    PlusDict = viewCache.get("PlusData", {})
+    return PropDict, PlusDict
 
 def __GetEquipShowIDList(curPlayer):
     ## 获取外观装备ID列表
@@ -295,23 +308,6 @@
             continue
         equipShowIDList.append(curEquip.GetItemTypeID())
     return equipShowIDList
-
-## 灵宠信息
-def __GetPetInfo(curPlayer):
-    petInfo = {}
-    petClassLVList = []
-    equipPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptPet)
-    for index in xrange(equipPack.GetCount()):
-        packItem = equipPack.GetAt(index)
-        if not packItem or packItem.IsEmpty():
-            continue
-        petNPCID = packItem.GetUserAttr(ShareDefine.Def_IudetPet_NPCID)
-        classLV = packItem.GetUserAttr(ShareDefine.Def_IudetPet_ClassLV)
-        petClassLVList.append({"id":petNPCID, 'lv':classLV})
-        
-    petInfo["PetLV"] = petClassLVList
-    petInfo["AtkSpeed"] = GameObj.GetAtkSpeed(curPlayer)
-    return petInfo
 
 ## 坐骑信息
 def __GetHorseInfo(curPlayer):
@@ -414,16 +410,14 @@
     NetPackCommon.SendPyPackToGameServer(sendPack)  
     return
 
-def GetPlayerPropData(findPlayerID):
-    ## 获取玩家战斗属性数据  UpdPlayerPropPlusCache
-    viewCache = PyGameData.g_playerViewCache.get(findPlayerID, {})
-    return viewCache.get("PropData", {})
-
-def GetPlayerPropDataCall(curPlayer, findPlayerID, callFunc, callData=None, syncClient=True):
+def GetPlayerPropDataCall(curPlayer, findPlayerID, callFunc, callData=None, syncClient=False):
     ## 获取玩家战斗属性数据 - 支持callback,因为地图可能暂时没有离线玩家缓存数据
+    # @param callFunc: 获取到玩家数据后回调函数,可能地图没有数据,会等向GameServer同步过后再回调
+    # @param callData: 回调函数扩展参数,透传参数
+    # @param syncClient: 是否附带通知前端该玩家数据,一般如果有需要前端配合展示的,需要直接传给前端,如镜像PK类功能
     
     playerID = curPlayer.GetPlayerID()
-    dataDict = GetPlayerPropData(findPlayerID)
+    dataDict = GetPlayerPropPlusDictByID(findPlayerID, True)[0]
     if dataDict:
         if syncClient:
             Sync_PlayerCache(curPlayer, findPlayerID)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py
index 9d5fe12..16e4852 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py
@@ -624,11 +624,11 @@
     if skillTag != ChConfig.Def_UseSkillTag_PetMaster:
         return False
 
-    if not PetControl.IsPet(curNPC):
+    if not PetControl.IsPetNPC(curNPC):
         GameWorld.ErrLog("NPCID %s AI %s 非宠物,无法获得主人释放技能"%(curNPC.GetNPCID(), curNPC.GetAIType()))
         return False
     
-    petOwner = PetControl.GetPetOwner(curNPC)
+    petOwner = PetControl.GetPetNPCOwner(curNPC)
     
     if petOwner == None:
         GameWorld.ErrLog("宠物(%s)对主人释放技能,找不到主人"%curNPC.GetRolePet().PetID)
@@ -1757,11 +1757,11 @@
                   
         # 宠物对主人释放技能
         elif skillAffectTag == ChConfig.Def_UseSkillTag_PetMaster:
-            if not PetControl.IsPet(attacker):
+            if not PetControl.IsPetNPC(attacker):
                 GameWorld.ErrLog("该NPC非宠物,无法获得主人释放技能")
                 return False
             
-            petOwner = PetControl.GetPetOwner(attacker)
+            petOwner = PetControl.GetPetNPCOwner(attacker)
             
             if petOwner == None:
                 GameWorld.ErrLog("宠物(%s)对主人释放技能,找不到主人"%attacker.GetRolePet().PetID)
@@ -3844,10 +3844,10 @@
     
     else:
         if affectTag == ChConfig.Def_UseSkillTag_PetMaster:
-            if not PetControl.IsPet(attacker):
+            if not PetControl.IsPetNPC(attacker):
                 return False
             
-            petOwner = PetControl.GetPetOwner(attacker)
+            petOwner = PetControl.GetPetNPCOwner(attacker)
             if petOwner == None:
                 return False
             
@@ -3898,15 +3898,19 @@
 ## 获取技能总等级
 #  @param curPlayer
 #  @return allSkillLV:总技能等级
-def GetAllSkillLV(curPlayer, funcType):
-    allSkillLV = 0
+def GetAllSkillInfo(curPlayer, funcTypeList):
+    # @return: {funcType:{skillID:skillLV, ...}, ...}
+    skillDict = {}
     skillManager = curPlayer.GetSkillManager()
     for i in xrange(skillManager.GetSkillCount()):
         curPlayerSkill = skillManager.GetSkillByIndex(i)
         if curPlayerSkill == None:
             continue
-        if curPlayerSkill.GetFuncType() != funcType:
+        funcType = curPlayerSkill.GetFuncType()
+        if funcType not in funcTypeList:
             continue
-        skillLV = curPlayerSkill.GetSkillLV()
-        allSkillLV += skillLV
-    return allSkillLV
+        if funcType not in skillDict:
+            skillDict[funcType] = {}
+        skillLVDict = skillDict[funcType]
+        skillLVDict[curPlayerSkill.GetSkillID()] = curPlayerSkill.GetSkillLV()
+    return skillDict

--
Gitblit v1.8.0