From ad158391ff62df48198a5411e5950e578dc3c43c Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期四, 11 四月 2019 14:42:21 +0800
Subject: [PATCH] 6459 【后端】【2.0】缥缈仙域开发单(可进入跨服妖王地图支持分区,增加竞争归属逻辑)

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py                            |    6 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py                                       |    2 
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py                                                        |  184 +++++++
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py                                                     |   15 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py                                       |    8 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossDemonKing.py |  417 ++++++++++++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_OpenFB.py                |   69 ++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/EnterFB.py                               |    5 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCustomRefresh.py                              |   41 +
 ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py                                                            |    8 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_186.py                              |  297 -----------
 ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py                                                |   16 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py                   |    2 
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py                                           |    7 
 ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py                                                             |    5 
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBoss.py                                               |   59 +
 ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py                                                               |   10 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py                                   |    3 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py                                     |  348 +++++++++++++
 ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py                                                            |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py                   |   12 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py                              |   23 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                                          |   13 
 23 files changed, 1,218 insertions(+), 334 deletions(-)

diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py b/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
index a4e117b..6eba4d0 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
@@ -677,16 +677,24 @@
 Def_FBMapID_CrossRealmPK = 32010
 #跨服蓬莱仙境
 Def_FBMapID_CrossPenglai = 32020
+#跨服妖王
+Def_FBMapID_CrossDemonKing = 32030
 #骑宠Boss
 Def_FBMapID_HorsePetBoss = 31200
 
 #需要刷世界BOSS的副本
 WorldBossFBMapIDList = [Def_FBMapID_SealDemon, Def_FBMapID_ZhuXianBoss]
 #跨服地图
-Def_CrossMapIDList = [Def_FBMapID_CrossRealmPK, Def_FBMapID_CrossPenglai]
+Def_CrossMapIDList = [Def_FBMapID_CrossRealmPK, Def_FBMapID_CrossPenglai, Def_FBMapID_CrossDemonKing]
+#跨服分区类型配置, 没配置的默认 CrossZoneComm
+Def_CrossZoneTypeName = {Def_FBMapID_CrossPenglai:"CrossZoneComm",
+                         Def_FBMapID_CrossDemonKing:"CrossZonePK",
+                         }
 #跨服分区对应地图配置表名
 Def_CrossZoneMapTableName = {Def_FBMapID_CrossPenglai:"CrossPenglaiZoneMap",
                              }
+#需要动态分配线路的跨服地图
+Def_CrossDynamicLineMap = [Def_FBMapID_CrossDemonKing]
 
 #同系职业枚举
 JOB_TYPY_COUNT = 5
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
index ac28fe5..c1f129a 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
@@ -15508,6 +15508,7 @@
                   ("Cmd", c_ubyte),
                   ("SubCmd", c_ubyte),
                   ("DataMapID", c_int),    
+                  ("LineID", c_ushort),    
                   ]
 
     def __init__(self):
@@ -15525,6 +15526,7 @@
         self.Cmd = 0xC1
         self.SubCmd = 0x05
         self.DataMapID = 0
+        self.LineID = 0
         return
 
     def GetLength(self):
@@ -15537,12 +15539,14 @@
         DumpString = '''// C1 05 进入跨服地图 //tagCMEnterCrossServer:
                                 Cmd:%s,
                                 SubCmd:%s,
-                                DataMapID:%d
+                                DataMapID:%d,
+                                LineID:%d
                                 '''\
                                 %(
                                 self.Cmd,
                                 self.SubCmd,
-                                self.DataMapID
+                                self.DataMapID,
+                                self.LineID
                                 )
         return DumpString
 
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBoss.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBoss.py
index 283cdf3..bdf7df7 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBoss.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBoss.py
@@ -55,14 +55,22 @@
     ## 获取地图跨服boss所属分区
     if dataMapID not in ChConfig.Def_CrossMapIDList:
         return 0
-    if dataMapID not in ChConfig.Def_CrossZoneMapTableName:
-        GameWorld.ErrLog("跨服boss没有分区表!dataMapID=%s" % dataMapID)
+    # 固定线路分配的
+    if dataMapID in ChConfig.Def_CrossZoneMapTableName:
+        tableName = ChConfig.Def_CrossZoneMapTableName[dataMapID]
+        zoneIpyData = IpyGameDataPY.GetIpyGameData(tableName, realMapID, dataMapID, copyMapID)
+        if not zoneIpyData:
+            return 0
+        return zoneIpyData.GetZoneID()
+    
+    # 动态线路分配的
+    elif dataMapID in ChConfig.Def_CrossDynamicLineMap:
+        zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(dataMapID, {}) # {dataMapID:{(zoneID, funcLineID):[mapID, copyMapID], ...}, ...}
+        for zoneLineInfo, mapCopyInfo in zoneLineDict.items():
+            if realMapID == mapCopyInfo[0] and copyMapID == mapCopyInfo[1]:
+                return zoneLineInfo[0]
         return 0
-    tableName = ChConfig.Def_CrossZoneMapTableName[dataMapID]
-    zoneIpyData = IpyGameDataPY.GetIpyGameData(tableName, realMapID, dataMapID, copyMapID)
-    if not zoneIpyData:
-        return 0
-    return zoneIpyData.GetZoneID()
+    return 0
 
 def __GetCrossBossRecData(zoneID, bossID):
     ## 获取跨服Boss Rec数据
@@ -234,18 +242,21 @@
     
     curTime = int(time.time())
     if not PyGameData.g_sortBOSSRefreshList:
+        crossZoneName = GameWorld.GetCrossZoneName()
         ipyDataMgr = IpyGameDataPY.IPY_Data()
         for i in xrange(ipyDataMgr.GetBOSSInfoCount()):
             ipyData = ipyDataMgr.GetBOSSInfoByIndex(i)
             bossID = ipyData.GetNPCID()
             mapID = ipyData.GetMapID()
-            if mapID not in ChConfig.Def_CrossZoneMapTableName:
+            if mapID not in ChConfig.Def_CrossZoneTypeName:
                 continue
-            tableName = ChConfig.Def_CrossZoneMapTableName[mapID]
-            if not hasattr(ipyDataMgr, "Get%sCount" % tableName):
+            zoneTypeName = ChConfig.Def_CrossZoneTypeName[mapID]
+            if not hasattr(ipyDataMgr, "Get%sCount" % zoneTypeName):
                 continue
-            for i in xrange(getattr(ipyDataMgr, "Get%sCount" % tableName)()):
-                zoneIpyData = getattr(ipyDataMgr, "Get%sByIndex" % tableName)(i)
+            for i in xrange(getattr(ipyDataMgr, "Get%sCount" % zoneTypeName)()):
+                zoneIpyData = getattr(ipyDataMgr, "Get%sByIndex" % zoneTypeName)(i)
+                if zoneIpyData.GetCrossZoneName() != crossZoneName:
+                    continue
                 zoneID = zoneIpyData.GetZoneID()
                 bossRecData = __GetCrossBossRecData(zoneID, bossID)
                 killedTime = GetRecKilledTime(bossRecData)
@@ -275,24 +286,38 @@
         
     return
 
+def GetCrossBossIsAliveOrCanReborn(zoneID, bossID):
+    ##BOSS是否活着或者可重生
+    if __GetCrossBossIsAlive(zoneID, bossID):
+        return True
+    bossRecData = __GetCrossBossRecData(zoneID, bossID)
+    killedTime = GetRecKilledTime(bossRecData)
+    refreshTime = GetRecRefreshTime(bossRecData)    
+    curTime = int(time.time())
+    rebornSecond = max(0, refreshTime - (curTime - killedTime))
+    return rebornSecond == 0
+
 def OnCrossMapServerInitOK():
     __SendMapServerAliveCrossBoss()
     return
 
 def __SendMapServerAliveCrossBoss():
     ## 同步当前还活着的boss,防止地图重启后已经刷新的boss不刷新
+    crossZoneName = GameWorld.GetCrossZoneName()
     ipyDataMgr = IpyGameDataPY.IPY_Data()
     for i in xrange(IpyGameDataPY.IPY_Data().GetBOSSInfoCount()):
         ipyData = IpyGameDataPY.IPY_Data().GetBOSSInfoByIndex(i)
         bossID = ipyData.GetNPCID()
         mapID = ipyData.GetMapID()
-        if mapID not in ChConfig.Def_CrossZoneMapTableName:
+        if mapID not in ChConfig.Def_CrossZoneTypeName:
             continue
-        tableName = ChConfig.Def_CrossZoneMapTableName[mapID]
-        if not hasattr(ipyDataMgr, "Get%sCount" % tableName):
+        zoneTypeName = ChConfig.Def_CrossZoneTypeName[mapID]
+        if not hasattr(ipyDataMgr, "Get%sCount" % zoneTypeName):
             continue
-        for i in xrange(getattr(ipyDataMgr, "Get%sCount" % tableName)()):
-            zoneIpyData = getattr(ipyDataMgr, "Get%sByIndex" % tableName)(i)
+        for i in xrange(getattr(ipyDataMgr, "Get%sCount" % zoneTypeName)()):
+            zoneIpyData = getattr(ipyDataMgr, "Get%sByIndex" % zoneTypeName)(i)
+            if zoneIpyData.GetCrossZoneName() != crossZoneName:
+                continue
             zoneID = zoneIpyData.GetZoneID()
             isAlive = __GetCrossBossIsAlive(zoneID, bossID)
             if not isAlive:
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
index 47cad59..1cfe194 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
@@ -26,6 +26,7 @@
 import PlayerTalk
 import CrossBoss
 import ChConfig
+import PlayerFB
 import GMShell
 
 import traceback
@@ -89,6 +90,9 @@
             
         elif msgType == ShareDefine.ClientServerMsg_QueryNPCInfo:
             PlayerQuery.ClientServerMsg_QueryNPCInfo(serverGroupID, msgData)
+            
+        elif msgType == ShareDefine.ClientServerMsg_EnterFB:
+            PlayerFB.ClientServerMsg_EnterFB(serverGroupID, msgData)
             
         elif msgType == ShareDefine.ClientServerMsg_SetPlayerAttrValue:
             MapServer_CrossSetPlayerAttrValue(msgData)
@@ -246,6 +250,9 @@
         elif msgType == ShareDefine.CrossServerMsg_NPCInfoRet:
             PlayerQuery.CrossServerMsg_NPCInfoRet(msgData, tick)
             
+        elif msgType == ShareDefine.CrossServerMsg_EnterFBRet:
+            PlayerFB.CrossServerMsg_EnterFBRet(msgData, tick)
+            
         # 需要发送到地图服务器处理的
         elif msgType in [ShareDefine.CrossServerMsg_RebornRet, ShareDefine.CrossServerMsg_CollectNPCOK]:
             MapServer_ClientServerReceiveMsg(msgType, msgData)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py
index 5438e00..81b77ee 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py
@@ -61,6 +61,22 @@
     ipyData = IpyGameDataPY.GetIpyGameDataNotLog("CrossZoneComm", crossZoneName, zoneID)
     return ipyData
 
+def GetCrossZoneIpyDataByServerGroupID(mapID, serverGroupID):
+    ## 获取跨服分区
+    zoneTypeName = ChConfig.Def_CrossZoneTypeName.get(mapID, "CrossZoneComm")
+    crossZoneName = GameWorld.GetCrossZoneName()
+    ipyDataList = IpyGameDataPY.GetIpyGameDataByCondition(zoneTypeName, {"CrossZoneName":crossZoneName}, True)
+    if not ipyDataList:
+        return
+    for ipyData in ipyDataList:
+        serverGroupIDList = ipyData.GetServerGroupIDList()
+        for serverGroupIDInfo in serverGroupIDList:
+            if (isinstance(serverGroupIDInfo, tuple) and serverGroupIDInfo[0] <= serverGroupID <= serverGroupIDInfo[1]) \
+                or (isinstance(serverGroupIDInfo, int) and serverGroupIDInfo == serverGroupID):
+                return ipyData
+    GameWorld.ErrLog("没有找到跨服玩法对应分区! mapID=%s, serverGroupID=%s, zoneTypeName=%s" % (mapID, serverGroupID, zoneTypeName))
+    return
+
 def GetCrossCommZoneIpyDataByServerGroupID(serverGroupID):
     ## 获取跨服常规分区
     crossZoneName = GameWorld.GetCrossZoneName()
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
index dd9aa79..4b3114f 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
@@ -30,6 +30,12 @@
 import PlayerTeam
 import GameWorld
 import ChConfig
+import CrossRealmPlayer
+import CrossRealmMsg
+import ShareDefine
+import CrossBoss
+
+import random
 #---------------------------------------------------------------------
 
 #---------------------------------------------------------------------
@@ -70,6 +76,184 @@
             return dataMapID
     return mapID
 
+def ClientServerMsg_EnterFB(serverGroupID, msgData):
+    ## 收到子服请求进入动态分配的跨服副本
+    playerID = msgData["PlayerID"]
+    dataMapID = msgData["DataMapID"]
+    funcLineID = msgData["FuncLineID"]
+    
+    if dataMapID == ChConfig.Def_FBMapID_CrossDemonKing:
+        zoneIpyData = CrossRealmPlayer.GetCrossZoneIpyDataByServerGroupID(dataMapID, serverGroupID)
+        if not zoneIpyData:
+            return
+        zoneID = zoneIpyData.GetZoneID()
+        bossID = msgData["BossID"]
+        if not CrossBoss.GetCrossBossIsAliveOrCanReborn(zoneID, bossID):
+            GameWorld.DebugLog("当前跨服妖王死亡状态,不可进入! serverGroupID=%s,funcLineID=%s,zoneID=%s,bossID=%s" % (serverGroupID, funcLineID, zoneID, bossID))
+            return
+        
+        mapCopyLineInfo = __GetCrossDynamicLineInfo(dataMapID, funcLineID, zoneID)
+        if not mapCopyLineInfo:
+            return
+        mapID, copyMapID, isOpenNew = mapCopyLineInfo
+        
+        # 如果是等待线路启动中的直接返回,等启动好后再通知可进入
+        if __AddWaitCrossFBOpenPlayer(mapID, copyMapID, isOpenNew, playerID, serverGroupID):
+            return
+    else:
+        return
+    
+    playerIDList = [playerID]
+    retInfo = [playerIDList, dataMapID, mapID, copyMapID]
+    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterFBRet, retInfo, [serverGroupID])
+    return
+
+def CrossServerMsg_EnterFBRet(msgData, tick):
+    ## 收到跨服服务器动态分配的跨服副本进入信息
+    
+    playerIDList, dataMapID, mapID, copyMapID = msgData
+    
+    if dataMapID == ChConfig.Def_FBMapID_CrossDemonKing:
+        mapPosInfo = IpyGameDataPY.GetFuncEvalCfg("CrossDemonKingMap", 2)
+    else:
+        return
+    posX, posY = mapPosInfo
+    
+    for playerID in playerIDList:
+        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
+        if not curPlayer:
+            continue
+        CrossRealmPlayer.SendCrossRealmReg(curPlayer, dataMapID, mapID, dataMapID, copyMapID, posX, posY)
+        
+    return
+
+def __GetCrossDynamicLineInfo(dataMapID, funcLineID, zoneID):
+    ## 获取跨服分区对应动态分配的副本地图虚拟线路信息
+
+    isOpenNew = False    
+    zoneLineKey = (zoneID, funcLineID)
+    if dataMapID not in PyGameData.g_crossDynamicLineInfo:
+        PyGameData.g_crossDynamicLineInfo[dataMapID] = {}
+    zoneLineDict = PyGameData.g_crossDynamicLineInfo[dataMapID] # 跨服动态线路信息 {dataMapID:{(zoneID, funcLineID):[mapID, copyMapID], ...}, ...}    
+    if zoneLineKey in zoneLineDict:
+        mapID, copyMapID = zoneLineDict[zoneLineKey]
+        GameWorld.DebugLog("已存在该分区功能线路ID,不需要重新分配: zoneID=%s,funcLineID=%s,mapID=%s,copyMapID=%s" % (zoneID, funcLineID, mapID, copyMapID))
+        return mapID, copyMapID, isOpenNew
+    
+    if dataMapID == ChConfig.Def_FBMapID_CrossDemonKing:
+        mapIDList = IpyGameDataPY.GetFuncEvalCfg("CrossDemonKingMap", 1)
+        
+    # 其他地图待扩展
+    else:
+        return
+    
+    usedMapCopyList = zoneLineDict.values() # 已经使用中的地图虚拟线路
+    openMapID, openCopyMapID = 0, 0
+    for mapID in mapIDList:
+        maxCopyMapCount = PyGameData.g_crossMapCopyMapCountDict.get(mapID, 0)
+        for copyMapID in xrange(maxCopyMapCount):
+            if [mapID, copyMapID] not in usedMapCopyList:
+                openMapID, openCopyMapID = mapID, copyMapID
+                break
+        if openMapID:
+            break
+    if not openMapID:
+        GameWorld.ErrLog("没有空余的虚拟线路,无法动态开启跨服副本!dataMapID=%s, funcLineID=%s, zoneID=%s, mapIDList=%s" 
+                         % (dataMapID, funcLineID, zoneID, mapIDList))
+        return
+    isOpenNew = True
+    mapID, copyMapID = openMapID, openCopyMapID
+    
+    zoneLineDict[zoneLineKey] = [mapID, copyMapID]
+    propertyID = zoneID * 1000 + funcLineID
+    GameWorld.DebugLog("不存在该分区功能线路ID,重新分配: zoneID=%s,funcLineID=%s,mapID=%s,copyMapID=%s,propertyID=%s" 
+                       % (zoneID, funcLineID, mapID, copyMapID, propertyID))
+    
+    # 通知地图开启新的地图虚拟分线
+    msgInfo = str([copyMapID, propertyID])
+    GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, 0, 0, mapID, "OpenFB", msgInfo, len(msgInfo))
+    return mapID, copyMapID, isOpenNew
+
+def __AddWaitCrossFBOpenPlayer(mapID, copyMapID, isOpenNew, playerID, serverGroupID):
+    ## 添加跨服玩家进入等待动态副本虚拟线路开启队列
+    
+    if mapID not in PyGameData.g_crossDynamicLineOpeningInfo:
+        PyGameData.g_crossDynamicLineOpeningInfo[mapID] = {}
+    openingMapCopyIDDict = PyGameData.g_crossDynamicLineOpeningInfo[mapID] # 跨服动态线路正在开启中的线路信息 {mapID:{copyMapID:{playerID:serverGroupID, ...}, ...}, ...}
+    
+    if isOpenNew or copyMapID in openingMapCopyIDDict:
+        if copyMapID not in openingMapCopyIDDict:
+            openingMapCopyIDDict[copyMapID] = {}
+        waitingPlayerDict = openingMapCopyIDDict[copyMapID]
+        waitingPlayerDict[playerID] = serverGroupID
+        GameWorld.Log("添加玩家进入等待跨服动态副本虚拟线路开启队列: mapID=%s,copyMapID=%s,isOpenNew=%s,playerID=%s,serverGroupID=%s" 
+                      % (mapID, copyMapID, isOpenNew, playerID, serverGroupID))
+        GameWorld.Log("    PyGameData.g_crossDynamicLineOpeningInfo=%s" % PyGameData.g_crossDynamicLineOpeningInfo)
+        return True
+    return False
+
+def OnCrossDynamicLineOpen(mapID, copyMapID):
+    ## 动态分配线路的地图虚拟线路启动成功
+    
+    if mapID not in PyGameData.g_crossDynamicLineOpeningInfo:
+        return
+    openingCopyMapDict = PyGameData.g_crossDynamicLineOpeningInfo[mapID]
+    waitingPlayerDict = openingCopyMapDict.pop(copyMapID, {})
+    if not waitingPlayerDict:
+        return
+    
+    # 通知子服等待中的玩家可以进入副本
+    serverPlayerIDListDict = {}
+    for playerID, serverGroupID in waitingPlayerDict.items():
+        if serverGroupID not in serverPlayerIDListDict:
+            serverPlayerIDListDict[serverGroupID] = []
+        playerIDList = serverPlayerIDListDict[serverGroupID]
+        playerIDList.append(playerID)
+        
+    dataMapID = GetRecordMapID(mapID)
+    GameWorld.Log("动态分配虚拟线路启动成功,通知子服等待玩家可进入: dataMapID=%s,mapID=%s,copyMapID=%s,serverPlayerIDListDict=%s" 
+                  % (dataMapID, mapID, copyMapID, serverPlayerIDListDict))
+    for serverGroupID, playerIDList in serverPlayerIDListDict.items():
+        retInfo = [playerIDList, dataMapID, mapID, copyMapID]
+        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterFBRet, retInfo, [serverGroupID])
+        
+    GameWorld.DebugLog("    PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo)
+    GameWorld.DebugLog("    PyGameData.g_crossDynamicLineOpeningInfo=%s" % PyGameData.g_crossDynamicLineOpeningInfo)
+    return
+
+def OnCrossDynamicLineClose(mapID, copyMapID):
+    ## 动态分配线路的地图虚拟线路关闭
+        
+    dataMapID = GetRecordMapID(mapID)
+    GameWorld.Log("动态分配虚拟线路关闭 dataMapID=%s,mapID=%s,copyMapID=%s" % (dataMapID, mapID, copyMapID))
+    zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(dataMapID, {}) 
+    for key, mapCopyInfo in zoneLineDict.items():
+        if mapCopyInfo[0] == mapID and mapCopyInfo[1] == copyMapID:
+            zoneLineDict.pop(key)
+            break
+        
+    openingCopyMapDict = PyGameData.g_crossDynamicLineOpeningInfo.get(mapID, {})
+    openingCopyMapDict.pop(copyMapID, {})
+    
+    GameWorld.DebugLog("    PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo)
+    GameWorld.DebugLog("    PyGameData.g_crossDynamicLineOpeningInfo=%s" % PyGameData.g_crossDynamicLineOpeningInfo)
+    return
+
+def OnCrossDynamicMapReset(mapID, copyMapCount):
+    ## 动态分配线路的地图重置
+    
+    dataMapID = GetRecordMapID(mapID)
+    GameWorld.Log("动态分配虚拟线路地图重置 dataMapID=%s,mapID=%s,copyMapCount=%s" % (dataMapID, mapID, copyMapCount))
+    PyGameData.g_crossMapCopyMapCountDict[mapID] = copyMapCount
+    
+    zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(dataMapID, {}) 
+    for key, mapCopyInfo in zoneLineDict.items():
+        if mapCopyInfo[0] == mapID:
+            zoneLineDict.pop(key)
+            
+    PyGameData.g_crossDynamicLineOpeningInfo.pop(mapID, None)
+    return
+
 ## 请求进入副本分线
 #  @param curPlayer: 请求玩家
 #  @param queryCallName: 请求回调名
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
index 7672c0a..ffb1c07 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
@@ -512,6 +512,21 @@
         PlayerControl.CrossNotify(serverGroupIDList, crossNotifyList)
         return
     
+    if callName == "DynamicLineMapOpen":
+        realMapID, copyMapID = eval(resultName)
+        PlayerFB.OnCrossDynamicLineOpen(realMapID, copyMapID)
+        return
+    
+    if callName == "DynamicLineMapClose":
+        realMapID, copyMapID = eval(resultName)
+        PlayerFB.OnCrossDynamicLineClose(realMapID, copyMapID)
+        return
+    
+    if callName == "DynamicLineMapInitOK":
+        realMapID, copyMapCount = eval(resultName)
+        PlayerFB.OnCrossDynamicMapReset(realMapID, copyMapCount)
+        return
+    
     if callName == "CommMapServerInitOK":
         dataMapID, lineID, realMapID, copyMapID = eval(resultName)
         PyGameData.g_commMapLineInfo[(dataMapID, lineID)] = (realMapID, copyMapID)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py b/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
index bf021c6..45bed4c 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
@@ -100,3 +100,8 @@
 
 g_crossSetPlayerAttr = {} # 跨服玩家更新玩家数据 {playerID:[[setType, setDict], ...], ...}
 
+g_crossMapCopyMapCountDict = {} # 跨服地图动态分配虚拟线路条数信息 {mapID:copyMapCount, ...}
+g_crossDynamicLineInfo = {} # 跨服动态线路信息 {dataMapID:{(zoneID, funcLineID):[mapID, copyMapID], ...}, ...}
+g_crossDynamicLineOpeningInfo = {} # 跨服动态线路正在开启中的线路信息 {mapID:{copyMapID:{playerID:serverGroupID, ...}, ...}, ...}
+
+
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py b/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
index c2d09f5..063ab36 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
@@ -1194,6 +1194,7 @@
 CrossServerMsg_RebornRet = "RebornRet"                  # 复活结果
 CrossServerMsg_NPCInfoRet = "NPCInfoRet"                # 跨服地图NPC信息
 CrossServerMsg_CollectNPCOK = "CollectNPCOK"            # 采集NPC完成
+CrossServerMsg_EnterFBRet = "EnterFBRet"                # 请求进入跨服副本返回信息
 
 # 子服发送跨服信息定义
 ClientServerMsg_ServerInitOK = "ServerInitOK"           # 子服启动成功
@@ -1208,6 +1209,7 @@
 ClientServerMsg_QueryNPCInfo = "QueryNPCInfo"           # 查询跨服地图NPC信息
 ClientServerMsg_SetPlayerAttrValue = "SetPlayerAttrValue" # 玩家属性数值更新
 ClientServerMsg_CollectNPC = "CollectNPC"               # 采集NPC
+ClientServerMsg_EnterFB = "EnterFB"                     # 请求进入跨服副本
 
 #跨服广播类型定义
 CrossNotify_World = "World"
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 56f7b96..f637097 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
@@ -288,6 +288,8 @@
     #if FBLogic.DoFBOnKill_Player_ValuePrize(curPlayer, defender, tick):
     __GiveKill_Player_ValuePrize(curPlayer, defender, tick)
     
+    NPCCommon.OnPlayerKillNPCPlayer(curPlayer, defender, tick)
+    
     #执行副本杀人逻辑
     if FBLogic.DoFBOnKill_Player(curPlayer, defender, tick):
         return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index 6352200..d4d68fa 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -1772,6 +1772,8 @@
 Def_FBMapID_CrossRealmPK = 32010
 #跨服蓬莱仙境
 Def_FBMapID_CrossPenglai = 32020
+#跨服妖王
+Def_FBMapID_CrossDemonKing = 32030
 #多仙盟Boss
 Def_FBMapID_AllFamilyBoss = 31260
 #骑宠Boss
@@ -1780,12 +1782,14 @@
 Def_FBMapID_FairyTreasure = 31190
 
 #注册上传跨服服务器数据后直接进入跨服服务器的地图
-RegisterEnter_CrossServerMapIDList = [Def_FBMapID_CrossPenglai]
+RegisterEnter_CrossServerMapIDList = [Def_FBMapID_CrossPenglai, Def_FBMapID_CrossDemonKing]
 #跨服地图
-Def_CrossMapIDList = [Def_FBMapID_CrossRealmPK, Def_FBMapID_CrossPenglai]
+Def_CrossMapIDList = [Def_FBMapID_CrossRealmPK, Def_FBMapID_CrossPenglai, Def_FBMapID_CrossDemonKing]
 #跨服分区对应地图配置表名
 Def_CrossZoneMapTableName = {Def_FBMapID_CrossPenglai:"CrossPenglaiZoneMap",
                              }
+#需要动态分配线路的跨服地图
+Def_CrossDynamicLineMap = [Def_FBMapID_CrossDemonKing]
 
 #副本关闭时未拾取的物品邮件发放给玩家
 #这里只有需要的副本才配置,不做默认逻辑,防止某些副本实际不能给导致刷物品,如麒麟之府
@@ -1874,6 +1878,7 @@
                 'SealDemon':[Def_FBMapID_SealDemon, Def_FBMapID_SealDemonEx], #封魔坛
                 'XMZZ':[Def_FBMapID_XMZZ], #仙魔之争
                 'CrossRealmPK':[Def_FBMapID_CrossRealmPK], #跨服竞技场
+                'CrossDemonKing':[Def_FBMapID_CrossDemonKing], #跨服妖王
                 'GatherSoul':[Def_FBMapID_GatherSoul],#聚魂副本
                 'ZhuXianBoss':[Def_FBMapID_ZhuXianBoss],#诛仙BOSS
                 'ZhuXianTower':[Def_FBMapID_ZhuXianTower],#诛仙塔
@@ -2094,7 +2099,8 @@
 DropOwnerType_Faction, # 阵营 5
 DropOwnerType_Special, # 特殊 6
 DropOwnerType_Family, # 仙盟 7
-) = range(8)
+DropOwnerType_Contend, # 争夺 8 第一个攻击的获得归属,击杀当前归属者的玩家成为新归属者
+) = range(9)
 
 #------------------------------------------------
 #技能类型
@@ -3116,6 +3122,7 @@
 Def_PlayerKey_FBCommendFightPower = "FBCommendFightPower" # 副本推荐战斗力
 Def_PlayerKey_RecordXPValue = "RecordXPValue"    #临时记录XP值
 Def_PlayerKey_CollectNPCObjID = "CollectNPCObjID"    #采集的NPC对象id
+Def_PlayerKey_ContendNPCObjID = "ContendNPCObjID"    #竞争归属的NPC实例ID
 Def_PlayerKey_AreaRewardMultiple = "AreaRewardMultiple"    #玩家所在区域福利倍值,默认1
 Def_PlayerKey_AttrActivatyNotify = "AttrActivatyNotify" # 属性激活提示类型
 Def_PlayerKey_AttrActivatyRecordStarLV = "AttrActivatyRecordStarLV" # 属性激活记录 - 强化星级
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
index ac28fe5..c1f129a 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
@@ -15508,6 +15508,7 @@
                   ("Cmd", c_ubyte),
                   ("SubCmd", c_ubyte),
                   ("DataMapID", c_int),    
+                  ("LineID", c_ushort),    
                   ]
 
     def __init__(self):
@@ -15525,6 +15526,7 @@
         self.Cmd = 0xC1
         self.SubCmd = 0x05
         self.DataMapID = 0
+        self.LineID = 0
         return
 
     def GetLength(self):
@@ -15537,12 +15539,14 @@
         DumpString = '''// C1 05 进入跨服地图 //tagCMEnterCrossServer:
                                 Cmd:%s,
                                 SubCmd:%s,
-                                DataMapID:%d
+                                DataMapID:%d,
+                                LineID:%d
                                 '''\
                                 %(
                                 self.Cmd,
                                 self.SubCmd,
-                                self.DataMapID
+                                self.DataMapID,
+                                self.LineID
                                 )
         return DumpString
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/EnterFB.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/EnterFB.py
index 511fd18..a228fff 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/EnterFB.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/EnterFB.py
@@ -37,6 +37,9 @@
     posY = paramList[3] if len(paramList) > 3 else 0
     if FBCommon.GetFBPDictValue(curPlayer, ChConfig.Def_PDict_LastEnterFBTick % mapID):
         FBCommon.SetFBPDictValue(curPlayer, ChConfig.Def_PDict_LastEnterFBTick % mapID, 0)
-    PlayerControl.PlayerEnterFB(curPlayer, mapID, lineID, posX, posY)
+    if mapID in ChConfig.Def_CrossMapIDList:
+        PlayerControl.PlayerEnterCrossServer(curPlayer, mapID, lineID)
+    else:
+        PlayerControl.PlayerEnterFB(curPlayer, mapID, lineID, posX, posY)
     return
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
index 97aaa8b..a80245c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
@@ -1229,7 +1229,11 @@
 def PlayerLoginInFBCheck(curPlayer, tick):
     gameMap = GameWorld.GetMap()
     #如果此地图是自动释放的, 需要检查这个玩家
-    if gameMap.GetMapFBType() in [IPY_GameWorld.fbtNull, IPY_GameWorld.fbtCrossVSRoom]:
+    if gameMap.GetMapFBType() in [IPY_GameWorld.fbtNull]:
+        return False
+    
+    #跨服服务器是直接注册的地图ID数据,地图肯定没有该玩家,所以不判断
+    if GameWorld.IsCrossServer():
         return False
     
     #玩家 在副本中,并且副本不踢出玩家下线
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossDemonKing.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossDemonKing.py
new file mode 100644
index 0000000..f43e4a5
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossDemonKing.py
@@ -0,0 +1,417 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package GameWorldLogic.FBProcess.GameLogic_CrossDemonKing
+#
+# @todo:跨服妖王
+# @author hxp
+# @date 2019-04-11
+# @version 1.0
+#
+# 详细描述: 跨服妖王
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2019-04-11 14:30"""
+#-------------------------------------------------------------------------------
+
+import FBCommon
+import GameWorld
+import IPY_GameWorld
+import IpyGameDataPY
+import ChConfig
+import PyGameData
+import NPCCommon
+import ItemCommon
+import ShareDefine
+import PlayerSuccess
+import PlayerActLogin
+
+FBDict_StartTick = 'FBDict_StartTick%s' #开始时间
+FBDict_Speed = 'FBDict_Speed%s' #掉血速度 /s
+FBDict_RemainHP = 'FBDict_RemainHP%s' #剩余时间
+FBDict_IsOver = 'FBDict_IsOver' #是否已结算, 结算时的tick
+FBDict_IsReduceing = 'FBDict_IsReduceing%s' #是否掉血中
+FBDict_BossTotalHP = 'FBDict_BossTotalHP%s' #BOSS血量,需要的总时间
+FBDict_LastHurtTick = 'FBDict_LastHurtTick'  #上次伤害时间
+
+g_heroHurtDict = {} #{playerID:hurt}
+
+
+## 是否能够通过活动查询进入
+def OnEnterFBEvent(curPlayer, mapID, lineID, tick):
+    return True
+
+## 查询是否可以进入地图
+def OnChangeMapAsk(ask, tick):
+    return IPY_GameWorld.cmeAccept
+
+## 开启副本
+def OnOpenFB(tick):
+    lineID = GetCurFBFuncLineID()
+    killTime = 60
+    GameWorld.GetGameWorld().SetGameWorldDict(FBDict_BossTotalHP % lineID, killTime * 1000)
+    return
+
+## 进副本
+def DoEnterFB(curPlayer, tick):
+    playerID = curPlayer.GetPlayerID()
+    zoneID = GetCurFBLineZoneID()
+    funcLineID = GetCurFBFuncLineID()
+    GameWorld.DebugLog("DoEnterFB zoneID=%s,funcLineID=%s" % (zoneID, funcLineID), playerID)
+    return
+
+## 关闭副本
+def OnCloseFB(tick):
+#    gameWorld = GameWorld.GetGameWorld()
+#    lineID = gameWorld.GetPropertyID() - 1
+#    gameWorld.SetGameWorldDict(FBDict_StartTick % lineID, 0)
+#    gameWorld.SetGameWorldDict(FBDict_Speed % lineID, 0)
+#    gameWorld.SetGameWorldDict(FBDict_RemainHP % lineID, 0)
+#    
+#    gameWorld.SetPropertyID(0)
+    return
+
+## 玩家退出副本
+def DoExitFB(curPlayer, tick):
+    UpdateHPReduceSpeed(tick, True)
+    return
+
+##玩家主动离开副本.
+def DoPlayerLeaveFB(curPlayer, tick):
+#    FBCommon.SetHadDelTicket(curPlayer, 0)
+#    #主动退出的去掉排行榜信息
+#    lineID = GameWorld.GetGameWorld().GetPropertyID() - 1
+#    playerHurtDict = PyGameData.g_ZhuXianBossPlayerHurtDict.get(lineID, {})
+#    playerHurtDict.pop(curPlayer.GetPlayerID(), 0)
+#    PyGameData.g_ZhuXianBossPlayerHurtDict[lineID] = playerHurtDict
+#    if not playerHurtDict: #榜上没人,停止掉血
+#        StopReduceHP(lineID, tick)
+    return
+
+## 是否副本复活
+def OnPlayerReborn():
+    return True
+
+## 获得副本帮助信息
+def DoFBHelp(curPlayer, tick):
+    
+    if GameWorld.GetGameFB().GetGameFBDictByKey(FBDict_IsOver):
+        return
+    
+    lineID = GetCurFBFuncLineID()
+    curSpeed = GameWorld.GetGameWorld().GetGameWorldDictByKey(FBDict_Speed % lineID) 
+    isReduceing = GameWorld.GetGameWorld().GetGameWorldDictByKey(FBDict_IsReduceing % lineID) 
+    remainHP = GetBossRemainHP(lineID, tick)
+    totalHP = __GetBossTotalHP(lineID)
+    hpReduceSpeed = curSpeed * 10000 / totalHP if totalHP else 0
+    remainHPPer = min(1000000, remainHP * 1000000 / totalHP) if totalHP else 0
+    fbHelpDict = {FBCommon.Help_lineID:lineID, 'hpReduceSpeed':hpReduceSpeed, 'remainHPPer':remainHPPer, 'isReduceing':isReduceing}
+    GameWorld.DebugLog("DoFBHelp: %s" % fbHelpDict, curPlayer.GetPlayerID())
+    FBCommon.Notify_FBHelp(curPlayer, fbHelpDict)
+    return
+
+## 玩家对NPC造成伤害
+def DoFB_Player_HurtNPC(curPlayer, curNPC, hurtHP):
+    return
+    lineID = GetCurFBFuncLineID()
+    #有人上榜开始掉血
+    StartReduceHP(lineID, GameWorld.GetGameWorld().GetTick())
+    GameWorld.GetGameFB().SetGameFBDict(FBDict_LastHurtTick, GameWorld.GetGameWorld().GetTick())
+    return
+
+##---副本总逻辑计时器---
+# @param tick:时间戳
+# @return 无意义
+# @remarks 副本总逻辑计时器
+def OnProcess(tick):
+    return
+    gameFB = GameWorld.GetGameFB()
+    overTick = gameFB.GetGameFBDictByKey(FBDict_IsOver)
+    # 结算20秒后强制关闭副本, 防止玩家不捡东西导致不结算,强关后地板上的东西会邮件发放给玩家
+    if overTick and tick - overTick >= ChConfig.Def_FBPickupItemTime:
+        GameWorld.Log("强制踢出玩家关闭副本: overTick=%s,tick=%s" % (overTick, tick))
+        FBCommon.DoLogic_FBKickAllPlayer()
+        return
+
+    lineID = GetCurFBFuncLineID()
+    if lineID < 0:
+        return
+    gameWorld = GameWorld.GetGameWorld()
+    startTick = gameWorld.GetGameWorldDictByKey(FBDict_StartTick % lineID)
+    if not startTick or overTick:
+        return
+    lastHurtTick = gameFB.GetGameFBDictByKey(FBDict_LastHurtTick)
+    if lastHurtTick and tick - lastHurtTick >= 2000:
+        StopReduceHP(lineID, tick)
+        GameWorld.GetGameFB().SetGameFBDict(FBDict_LastHurtTick, 0)
+        
+    FBCommon.NotifyCopyMapPlayerFBHelp(tick, DoFBHelp, 5000)
+    __CheckBossHP(tick)
+    return
+
+def __DoLogicZhuXianBossOver(isPass, tick, dropPosX, dropPosY):
+    #结算
+#    gameFB = GameWorld.GetGameFB()
+#    mapID = GameWorld.GetMap().GetMapID()
+#    lineID = GameWorld.GetGameWorld().GetPropertyID() - 1
+#    leaveTick = FBCommon.GetFBLineStepTime(mapID, lineID) * 1000
+#    playerHurtList = __GetSortHurtList(lineID)
+#    if not playerHurtList:
+#        GameWorld.Log(' __DoLogicZhuXianBossOver, 伤害榜上没有人!!lineID=%s' % lineID)
+#        return
+#    firsthurtInfo = playerHurtList[0]
+#    firstPlayerID = firsthurtInfo[0]
+#    firstPlayerFamilyID = firsthurtInfo[1][2]
+#    playerManager = GameWorld.GetMapCopyPlayerManager()#GameWorld.GetPlayerManager()
+#    firstPlayer = playerManager.FindPlayerByID(firstPlayerID)
+#    if firstPlayer:
+#        gameFB.SetPlayerGameFBDict(firstPlayerID, FBPlayerDict_Rank, 1)
+#        if not dropPosX or not dropPosY:
+#            dropPosX, dropPosY = firstPlayer.GetPosX(), firstPlayer.GetPosY()
+#        prizeItemList = GiveZhuXianBossAward(firstPlayer, lineID, dropItemMapInfo=[dropPosX, dropPosY, True, True])
+#        if not prizeItemList:
+#            # 没有掉落时直接通知结算,防止卡副本
+#            firstPlayer.Sync_TimeTick(IPY_GameWorld.tttLeaveMap, 0, leaveTick, True)
+#            overDict = {FBCommon.Over_rank:1, FBCommon.Over_itemInfo:prizeItemList}
+#            FBCommon.NotifyFBOver(firstPlayer, ChConfig.Def_FBMapID_ZhuXianBoss, lineID, isPass, overDict)
+#        else:
+#            firstPlayer.Sync_TimeTick(ChConfig.tttPickupItem, 0, ChConfig.Def_FBPickupItemTime, True)
+#    else:
+#        leaveServerTick = PlayerControl.GetPlayerLeaveServerTick(firstPlayerID)
+#        if leaveServerTick and tick - leaveServerTick < ChConfig.Def_PlayerOfflineProtectTime:
+#            #离线超过3分钟的不给奖励
+#            msgStr = str([ShareDefine.Def_UniversalGameRecType_ZhuXianBossRecord, [firstPlayerID, lineID], [], 0, 0])
+#            GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, 'AddUniversalGameRec', msgStr, len(msgStr))
+#            
+#    helpItemList = FBCommon.GetFBLineReward(mapID, lineID)
+#    if helpItemList: #同盟协助奖励
+#        jsonItemList = FBCommon.GetJsonItemList(helpItemList)
+#        for index in range(0 , playerManager.GetPlayerCount()):
+#            curPlayer = playerManager.GetPlayerByIndex(index)
+#            curPlayerID = curPlayer.GetPlayerID()
+#            if not curPlayerID:
+#                continue
+#            if curPlayerID == firstPlayerID:
+#                continue
+#            curPlayer.Sync_TimeTick(IPY_GameWorld.tttLeaveMap, 0, leaveTick, True)
+#            remainCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_ZhuXianBossHelpCnt)
+#            if curPlayer.GetFamilyID() == firstPlayerFamilyID and remainCnt:
+#                PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_ZhuXianBossHelpCnt, remainCnt - 1)
+#                ItemControler.GivePlayerItemOrMail(curPlayer, helpItemList, 'ZXBossHelperReward')
+#                overDict = {FBCommon.Over_rank:0, FBCommon.Over_itemInfo:jsonItemList}
+#                FBCommon.NotifyFBOver(curPlayer, ChConfig.Def_FBMapID_ZhuXianBoss, lineID, isPass, overDict)
+#            else:
+#                overDict = {FBCommon.Over_rank:0}
+#                FBCommon.NotifyFBOver(curPlayer, ChConfig.Def_FBMapID_ZhuXianBoss, lineID, 0, overDict)
+            
+    return
+
+def GiveZhuXianBossAward(curPlayer, lineID, isMail=False, dropItemMapInfo=[]):
+    ##给归属者奖励
+    addCnt = 1
+    equipList = []
+    prizeItemDict = {}
+    bossID = GetCurFBLineBOSSID(lineID)
+
+    jsonItemList, totalExp, totalMoney = NPCCommon.GiveKillNPCDropPrize(curPlayer, ChConfig.Def_FBMapID_ZhuXianBoss, {bossID:addCnt},
+                                                                        mailTypeKey="ZXBossBelongerReward", isMail=isMail,
+                                                                        dropItemMapInfo=dropItemMapInfo)
+    for jsonItem in jsonItemList:
+        if 'UserData' in jsonItem:
+            equipList.append(jsonItem)
+        else:
+            itemID, itemCnt = jsonItem['ItemID'], jsonItem.get('Count', 1)
+            prizeItemDict[itemID] = prizeItemDict.get(itemID, 0) + itemCnt
+    
+    GameWorld.DebugLog("诛仙boss结算奖励: lineID=%s,bossID=%s,totalExp=%s,totalMoney=%s,jsonItemList=%s" 
+                       % (lineID, bossID, totalExp, totalMoney, jsonItemList), curPlayer.GetPlayerID())
+    
+    prizeItemList = equipList + FBCommon.GetJsonItemList(prizeItemDict.items())
+    #PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_ZhuXianBoss, addCnt)
+
+    #击杀特定NPC成就
+    PlayerSuccess.DoAddSuccessProgress(curPlayer, ShareDefine.SuccType_KillSpecificNPC, addCnt, [bossID])
+    FBCommon.AddEnterFBCount(curPlayer, ChConfig.Def_FBMapID_ZhuXianBoss, addCnt)
+    # 每日活动
+    #PlayerActivity.AddDailyActionFinishCnt(curPlayer, ShareDefine.DailyActionID_ZhuXianBoss, addCnt)
+    PlayerActLogin.AddLoginAwardActionCnt(curPlayer, ChConfig.Def_LoginAct_ZhuXianBOSS, addCnt)
+    return prizeItemList
+
+def OnPickUpItem(curPlayer, curItem, tick):
+    mapItemType = curItem.GetType()
+    if mapItemType == ChConfig.Def_ItemType_Money:
+        return
+    playerID = curPlayer.GetID()
+    isEquip = ItemCommon.CheckItemIsEquip(curItem)
+    jsonItem = ItemCommon.GetJsonItem(curItem)
+    if playerID in PyGameData.g_fbPickUpItemDict:
+        if isEquip:
+            PyGameData.g_fbPickUpItemDict[playerID].append(jsonItem)
+        else:
+            isIn = False
+            for itemInfo in PyGameData.g_fbPickUpItemDict[playerID]:
+                if itemInfo["ItemID"] == jsonItem["ItemID"] and itemInfo.get("IsBind") == jsonItem.get("IsBind"):
+                    itemInfo["Count"] = itemInfo.get("Count", 1) + jsonItem.get("Count", 1)
+                    isIn = True
+                    break
+            if not isIn:
+                PyGameData.g_fbPickUpItemDict[playerID].append(jsonItem)
+    else:
+        PyGameData.g_fbPickUpItemDict[playerID] = [jsonItem]
+    
+    playerItemCount = 0
+    mapItemManager = GameWorld.GetMapItemManager()
+    for index in xrange(mapItemManager.GetMapItemCount()):
+        mapItem = mapItemManager.GetMapItemByIndex(index)
+        if not mapItem or mapItem.IsEmpty():
+            continue
+        
+        # 还有属于自己的东西没捡不通知结束
+        if mapItem.GetOwnerID() == curPlayer.GetPlayerID():
+            playerItemCount += 1
+        
+    isItemAllPickUp = (playerItemCount <= 1)
+    if not isItemAllPickUp:
+        return
+    
+    isPass = 1
+    lineID = GameWorld.GetGameWorld().GetPropertyID() - 1
+    leaveTick = FBCommon.GetFBLineStepTime(ChConfig.Def_FBMapID_ZhuXianBoss, lineID) * 1000
+    gameFB = GameWorld.GetGameFB()
+    rank = 1###gameFB.GetPlayerGameFBDictByKey(playerID, FBPlayerDict_Rank)
+    
+    jsonItemList = PyGameData.g_fbPickUpItemDict.get(playerID, [])
+    curPlayer.Sync_TimeTick(IPY_GameWorld.tttLeaveMap, 0, leaveTick, True)
+    overDict = {FBCommon.Over_rank:rank, FBCommon.Over_itemInfo:jsonItemList}
+    FBCommon.NotifyFBOver(curPlayer, ChConfig.Def_FBMapID_ZhuXianBoss, lineID, isPass, overDict)
+    return
+
+
+def __CheckBossHP(tick):
+    gameFB = GameWorld.GetGameFB()
+    isOver = gameFB.GetGameFBDictByKey(FBDict_IsOver)
+    lineID = GameWorld.GetGameWorld().GetPropertyID() - 1
+   
+    if not isOver and GetBossRemainHP(lineID, tick) == 0:
+
+        bossID = GetCurFBLineBOSSID(lineID)
+        curBoss = GameWorld.FindNPCByNPCID(bossID)
+        dropPosX, dropPosY = 0, 0
+        if curBoss:
+            dropPosX, dropPosY = curBoss.GetPosX(), curBoss.GetPosY()
+        
+        #结束 设置BOSS死亡
+        FBCommon.ClearFBNPC()
+        FBCommon.NotifyCopyMapPlayerFBHelp(tick, DoFBHelp, 0)
+        GameWorld.DebugLog('结束 设置BOSS死亡 lineID=%s' % lineID)
+        ###playerHurtList = __GetSortHurtList(lineID)
+        playerHurtList = []
+        if playerHurtList:
+            killerName, hurtValue = playerHurtList[0][1][:2]
+            NPCCommon.GameServer_KillGameWorldBoss(bossID, killerName, hurtValue)
+            
+        NPCCommon.GameServe_GameWorldBossState(bossID, 0)
+            
+        ###__DoLogicZhuXianBossOver(1, tick, dropPosX, dropPosY)
+        gameFB.SetGameFBDict(FBDict_IsOver, tick)
+    return
+
+def UpdateHPReduceSpeed(tick, isExit=False):
+    gameWorld = GameWorld.GetGameWorld()
+    playerCnt = gameWorld.GetMapCopyPlayerManager().GetPlayerCount()
+    playerCnt = playerCnt - 1 if isExit else playerCnt
+    if playerCnt <= 0:
+        return
+    lineID = GameWorld.GetGameWorld().GetPropertyID() - 1
+    if lineID < 0:
+        return
+
+    curSpeed = int(min(1 + 0.08 * (playerCnt - 1), 1.8) * 1000)
+    gameWorld.SetGameWorldDict(FBDict_Speed % lineID, curSpeed)
+    if not gameWorld.GetGameWorldDictByKey(FBDict_IsReduceing % lineID):
+        return
+    
+    startTick = gameWorld.GetGameWorldDictByKey(FBDict_StartTick % lineID)
+    remainHP = gameWorld.GetGameWorldDictByKey(FBDict_RemainHP % lineID)
+    lastSpeed = gameWorld.GetGameWorldDictByKey(FBDict_Speed % lineID)
+    if not startTick:
+        startTick = tick 
+        lastSpeed = curSpeed
+        remainHP = __GetBossTotalHP(lineID)
+    remainHP = max(0, int((remainHP - (tick - startTick) / 1000.0 * lastSpeed)))
+    gameWorld.SetGameWorldDict(FBDict_StartTick % lineID, tick)
+    
+    gameWorld.SetGameWorldDict(FBDict_RemainHP % lineID, remainHP)
+    GameWorld.DebugLog('    curSpeed=%s, remainHP=%s, passTime=%s, lastSpeed=%s' % (curSpeed, remainHP, tick - startTick, lastSpeed))
+    FBCommon.NotifyCopyMapPlayerFBHelp(tick, DoFBHelp, 0)
+    return
+
+def StopReduceHP(lineID, tick):
+    ##暂停BOSS血量减少
+    gameWorld = GameWorld.GetGameWorld()
+    if not gameWorld.GetGameWorldDictByKey(FBDict_IsReduceing % lineID):
+        return
+    remainHP = GetBossRemainHP(lineID, tick)
+    if not remainHP:
+        return
+    gameWorld.SetGameWorldDict(FBDict_IsReduceing % lineID, 0)
+    gameWorld.SetGameWorldDict(FBDict_RemainHP % lineID, remainHP)
+    return
+
+def StartReduceHP(lineID, tick):
+    ##开始BOSS掉血
+    gameWorld = GameWorld.GetGameWorld()
+    if gameWorld.GetGameWorldDictByKey(FBDict_IsReduceing % lineID):
+        return
+    gameWorld.SetGameWorldDict(FBDict_IsReduceing % lineID, 1)
+    startTick = gameWorld.GetGameWorldDictByKey(FBDict_StartTick % lineID)
+    if not startTick:
+        gameWorld.SetGameWorldDict(FBDict_RemainHP % lineID, __GetBossTotalHP(lineID))
+    gameWorld.SetGameWorldDict(FBDict_StartTick % lineID, tick)
+    FBCommon.NotifyCopyMapPlayerFBHelp(tick, DoFBHelp, 0)
+    return
+
+def __GetBossTotalHP(lineID):return GameWorld.GetGameWorld().GetGameWorldDictByKey(FBDict_BossTotalHP % lineID)
+
+def GetBossRemainHP(lineID, tick):
+    gameWorld = GameWorld.GetGameWorld()
+    
+    startTick = gameWorld.GetGameWorldDictByKey(FBDict_StartTick % lineID)
+    lastSpeed = gameWorld.GetGameWorldDictByKey(FBDict_Speed % lineID) 
+    remainHP = gameWorld.GetGameWorldDictByKey(FBDict_RemainHP % lineID)
+    if not gameWorld.GetGameWorldDictByKey(FBDict_IsReduceing % lineID):
+        return remainHP
+    if not startTick:
+        startTick = tick
+        remainHP = __GetBossTotalHP(lineID)
+    else:
+        remainHP = max(0, int((remainHP - (tick - startTick) / 1000.0 * lastSpeed)))
+    return remainHP
+
+def GetCurFBLineBOSSID(lineID= -1):
+    #该分线刷的BOSSID
+    if lineID == -1:
+        lineID = GameWorld.GetGameWorld().GetPropertyID() - 1
+    if lineID == -1:
+        return 0
+    ipyData = IpyGameDataPY.GetIpyGameDataByCondition("FairyDomain", {"MapID":ChConfig.Def_FBMapID_CrossDemonKing, "LineID":lineID})
+    if not ipyData:
+        return 0
+    bossID = ipyData.GetBossID()
+    return bossID
+
+def GetCurFBFuncLineID(): return GameWorld.GetGameWorld().GetPropertyID() % 1000
+def GetCurFBLineZoneID(): return GameWorld.GetGameWorld().GetPropertyID() / 1000
+
+##玩家死亡.
+# @param curPlayer:死亡的玩家 
+# @param tick 时间戳
+# @return 返回值无意义
+# @remarks 玩家主动离开副本.
+def DoPlayerDead(curPlayer):
+    return
+
+##处理副本中杀死玩家逻辑
+def DoFBOnKill_Player(atkobj, defender, tick):
+    return True
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py
index 0b816e1..9143a93 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py
@@ -75,6 +75,10 @@
     
     #调用副本开启触发器
     FBLogic.OnOpenFB(tick)
+    
+    if gameWorld.GetMapID() in ChConfig.Def_CrossDynamicLineMap:
+        msgInfo = str([gameWorld.GetRealMapID(), gameWorld.GetCopyMapID()])
+        GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "DynamicLineMapOpen", msgInfo, len(msgInfo))
     return
 
 ## 副本关闭
@@ -167,6 +171,9 @@
     #根据是否收缩型FB处理
     FreeOrClearFBByAutoSize(gameWorld)
     
+    if gameWorld.GetMapID() in ChConfig.Def_CrossDynamicLineMap:
+        msgInfo = str([gameWorld.GetRealMapID(), gameWorld.GetCopyMapID()])
+        GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "DynamicLineMapClose", msgInfo, len(msgInfo))
     return
 
 ##根据表中的收缩类型释放或者清空副本状态
@@ -588,6 +595,11 @@
     if GameWorld.GetMap().GetMapFBType() == IPY_GameWorld.fbtNull:
         msgInfo = str([gameWorld.GetMapID(), gameWorld.GetLineID(), gameWorld.GetRealMapID(), gameWorld.GetCopyMapID()])
         GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "CommMapServerInitOK", msgInfo, len(msgInfo))
+        
+    if gameWorld.GetMapID() in ChConfig.Def_CrossDynamicLineMap and gameWorld.GetCopyMapID() == gameWorld.GetGameWorldCount() - 1:
+        msgInfo = str([gameWorld.GetRealMapID(), gameWorld.GetGameWorldCount()])
+        GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "DynamicLineMapInitOK", msgInfo, len(msgInfo))
+        
     return
 
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_186.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_186.py
index a928ed2..16c23cb 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_186.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_186.py
@@ -31,16 +31,10 @@
 import NPCCommon
 import AICommon
 import IPY_GameWorld
-import FamilyRobBoss
-import AttackCommon
 import GameWorld
 import BaseAttack
 import PlayerState
-import SkillCommon
-import PyGameData
-import BuffSkill
 import GameObj
-import ItemCommon
 
 ## 初始化
 #  @param curNPC 当前npc
@@ -81,7 +75,8 @@
         AICommon.NormalNPCFast_Move(curNPC, tick)
         return
     
-    tagObj = __RefreshDropOwner(curNPC, tick)
+    #默认攻击归属者
+    tagObj = npcControl.RefreshDropOwner(tick)
     
     if not curNPC.GetIsNeedProcess() or not tagObj:
         # 先回血,等回满再做其他事情
@@ -93,285 +88,6 @@
     __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 isDead:
-        GameWorld.Log("Boss死亡: lineID=%s,objID=%s,npcID=%s,dropOwnerType=%s" 
-                      % (GameWorld.GetGameWorld().GetLineID(), curNPC.GetID(), curNPC.GetNPCID(), dropOwnerType))
-    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, isDead)
-            elif ownerType == ChConfig.Def_NPCHurtTypePlayer:
-                tagObj = GameWorld.GetObj(ownerID, IPY_GameWorld.gotPlayer)
-                
-    elif dropOwnerType == ChConfig.DropOwnerType_Family:
-        ownerInfo = FamilyRobBoss.RefreshFamilyOwnerNPCHurt(npcControl, curNPC, tick, refreshInterval)
-        if ownerInfo:
-            tagObj, ownerFamilyID = ownerInfo
-            ownerType, ownerID = ChConfig.Def_NPCHurtTypeFamily, ownerFamilyID
-            
-    if isDead:
-        GameWorld.Log("ownerType=%s, ownerID=%s, tagObjID=%s" % (ownerType, ownerID, 0 if not tagObj else tagObj.GetPlayerID()))
-            
-    # 没有攻击目标,则刷新仇恨,支持主动怪
-    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 and not ownerType:
-            teamID = maxAngryObj.GetTeamID()
-            if teamID:
-                ownerType, ownerID = ChConfig.Def_NPCHurtTypeTeam, teamID
-            else:
-                ownerType, ownerID = ChConfig.Def_NPCHurtTypePlayer, maxAngryObj.GetPlayerID()
-        
-        if isDead:
-            GameWorld.Log("angryObj, ownerType=%s, ownerID=%s" % (ownerType, ownerID))
-            
-    __RefreshBossDropOwnerObjBuff(curNPC, npcControl, tick, ownerType, ownerID, isDead)
-    return tagObj
-
-def __GetMaxHurtTeamPlayer(curNPC, npcControl, teamID, isDead):
-    ## 获取最大伤血队伍中攻击的目标队员
-    curTeam = GameWorld.GetTeamManager().FindTeam(teamID)
-    if curTeam:
-        refreshPoint = curNPC.GetRefreshPosAt(curNPC.GetCurRefreshPointIndex())
-        if isDead:
-            GameWorld.Log("队伍成员数: teamID=%s,memberCount=%s" % (teamID, curTeam.GetMemberCount()))
-        for i in xrange(curTeam.GetMemberCount()):
-            curTeamPlayer = curTeam.GetMember(i)
-            if curTeamPlayer == None or curTeamPlayer.GetPlayerID() == 0:
-                if isDead:
-                    GameWorld.Log("    i=%s, 队员为空!" % i)
-                continue
-            if curTeamPlayer.GetHP() <= 0:
-                if isDead:
-                    GameWorld.Log("    i=%s, 队员血量为0!, memPlayerID=%s" % (i, curTeamPlayer.GetPlayerID()))
-                continue
-            if not curTeamPlayer.GetVisible():
-                if isDead:
-                    GameWorld.Log("    i=%s, 队员不可见!, memPlayerID=%s" % (i, curTeamPlayer.GetPlayerID()))
-                continue
-            if isDead:
-                GameWorld.Log("    i=%s, 队员坐标(%s, %s)! memPlayerID=%s" % (i, curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), curTeamPlayer.GetPlayerID()))
-            if npcControl.GetIsInRefreshPoint(curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), refreshPoint):
-                return curTeamPlayer
-    else:
-        GameWorld.ErrLog("找不到该队伍: teamID=%s" % teamID)
-    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, ChConfig.DropOwnerType_Family]:
-        #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)
-    
-    key = (GameWorld.GetGameWorld().GetLineID(), curNPC.GetID(), npcID)
-    if lastDropOwnerID and (lastDropOwnerType != ownerType or lastDropOwnerID != ownerID):
-        GameWorld.Log("归属变更, 清除旧归属! key=%s,ownerType=%s,ownerID=%s,lastDropOwnerType=%s,lastDropOwnerID=%s" 
-                      % (key, 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 isDead:
-        GameWorld.Log("Boss归属: key=%s,ownerType=%s,ownerID=%s" % (key, ownerType, ownerID))
-        
-    # 刷新归属
-    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
-        if isDead:
-            GameWorld.Log("队伍成员数: %s" % (curTeam.GetMemberCount()))
-        refreshPoint = curNPC.GetRefreshPosAt(curNPC.GetCurRefreshPointIndex())
-        for i in xrange(curTeam.GetMemberCount()):
-            curTeamPlayer = curTeam.GetMember(i)
-            if curTeamPlayer == None or curTeamPlayer.GetPlayerID() == 0:
-                if isDead:
-                    GameWorld.Log("    i=%s, 成员不存在!" % (i))
-                continue
-            
-            if curTeamPlayer.GetCopyMapID() == GameWorld.GetGameWorld().GetCopyMapID() \
-                and npcControl.GetIsInRefreshPoint(curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), refreshPoint) \
-                and AttackCommon.CheckKillNPCByCnt(curTeamPlayer, curNPC, False) and curTeamPlayer.GetVisible():
-                __AddBossDropOwnerPlayerBuff(curTeamPlayer, tick, curNPC)
-                killerDict[curTeamPlayer.GetPlayerID()] = curTeamPlayer
-                if isDead:
-                    GameWorld.Log("    i=%s, 成员有归属权! memPlayerID=%s,背包剩余空格=%s" 
-                                  % (i, curTeamPlayer.GetPlayerID(), ItemCommon.GetItemPackSpace(curTeamPlayer, IPY_GameWorld.rptItem)))
-                    
-            # 不同线、或者距离超出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:
-                    GameWorld.Log("    i=%s, 成员无归属权! memPlayerID=%s,copyMapID=%s,pos(%s,%s),CheckKillNPCByCnt=%s" 
-                                  % (i, curTeamPlayer.GetPlayerID(), curTeamPlayer.GetCopyMapID(), 
-                                     curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), 
-                                     AttackCommon.CheckKillNPCByCnt(curTeamPlayer, curNPC, False)))
-                    
-    elif ownerType == ChConfig.Def_NPCHurtTypeFamily:
-        
-        hurtType, hurtID = ChConfig.Def_NPCHurtTypeFamily, ownerID
-        refreshPoint = curNPC.GetRefreshPosAt(curNPC.GetCurRefreshPointIndex())
-        copyPlayerMgr = GameWorld.GetMapCopyPlayerManager()
-        for index in xrange(copyPlayerMgr.GetPlayerCount()):
-            player = copyPlayerMgr.GetPlayerByIndex(index)
-            if not player:
-                continue
-            
-            # 归属仙盟 且 在boss区域内
-            if player.GetFamilyID() == ownerID and npcControl.GetIsInRefreshPoint(player.GetPosX(), player.GetPosY(), refreshPoint) and player.GetVisible():
-                __AddBossDropOwnerPlayerBuff(player, tick, curNPC)
-                
-            else:
-                isOk = BuffSkill.DelBuffBySkillID(player, ChConfig.Def_SkillID_DropOwnerBuff, tick, buffOwner=curNPC)
-                if isOk:
-                    GameWorld.DebugLog("删除非归属仙盟成员buff: teamID=%s,playerID=%s" % (ownerID, player.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
-        elif ownerType == ChConfig.Def_NPCHurtTypeFamily:
-            PyGameData.g_npcKillerInfo[key] = {}, None, hurtType, hurtID
-            
-        GameWorld.Log("Boss被击杀: npcID=%s,key=%s,playerIDList=%s,teamID=%s,hurtType=%s,hurtID=%s" 
-                      % (npcID, key, killerDict.keys(), teamID, hurtType, hurtID))
-    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)
-    elif ownerType == ChConfig.Def_NPCHurtTypeFamily:
-        copyPlayerMgr = GameWorld.GetMapCopyPlayerManager()
-        for index in xrange(copyPlayerMgr.GetPlayerCount()):
-            player = copyPlayerMgr.GetPlayerByIndex(index)
-            if not player:
-                continue
-            __SetBossDropOwnerBuffDisappearTime(player, 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
 
 ## 每次被攻击处理结果
@@ -390,7 +106,8 @@
     dropOwnerType = NPCCommon.GetDropOwnerType(curNPC)
     if dropOwnerType not in [ChConfig.DropOwnerType_MaxHurt]:
         return True
-    tagObj = __RefreshDropOwner(curNPC, tick, 0)
+    npcControl = NPCCommon.NPCControl(curNPC)
+    tagObj = npcControl.RefreshDropOwner(tick, 0)
     if not atkObj or not tagObj:
         GameObj.SetHP(curNPC, 1)
         GameWorld.ErrLog("Boss当前状态下不可以死亡!npcID=%s" % curNPC.GetNPCID())
@@ -403,7 +120,8 @@
     PlayerState.SetBossStateTick(curPlayer, tick)
     
     #被击杀时强制刷新归属
-    __RefreshDropOwner(curNPC, tick, 0, True)
+    npcControl = NPCCommon.NPCControl(curNPC)
+    npcControl.RefreshDropOwner(tick, 0, True)
     return
 
 ## NPC死亡处理
@@ -417,7 +135,8 @@
     return
 
 def OnNPCSetDead(curNPC):
-    __DelayBossDropOwnerBuff(curNPC)
+    npcControl = NPCCommon.NPCControl(curNPC)
+    npcControl.DelayDropOwnerBuffDisappearTime()
     return
 
 ## npc攻击逻辑
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 787692c..fd34538 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
@@ -5086,6 +5086,354 @@
             return
         return curItem
     
+    ##----------------------------------------- 归属 -----------------------------------------------
+    
+    def RefreshDropOwner(self, tick, refreshInterval=3000, isDead=False):
+        ## 刷新boss掉落归属
+        # @return: 可攻击的掉落归属目标玩家
+        
+        curNPC = self.__Instance
+        tagObj = None # 即将攻击的目标, 归属最大伤血取最大伤血玩家或队伍队员,其他取最大仇恨
+        ownerType, ownerID = 0, 0
+        dropOwnerType = GetDropOwnerType(curNPC)
+        if isDead:
+            GameWorld.Log("Boss死亡: lineID=%s,objID=%s,npcID=%s,dropOwnerType=%s" 
+                          % (GameWorld.GetGameWorld().GetLineID(), curNPC.GetID(), curNPC.GetNPCID(), dropOwnerType))
+        if dropOwnerType == ChConfig.DropOwnerType_MaxHurt:
+            maxHurtObj = self.RefreshHurtList(tick, refreshInterval)
+            if maxHurtObj:
+                ownerType, ownerID = maxHurtObj.GetValueType(), maxHurtObj.GetValueID()
+                if ownerType == ChConfig.Def_NPCHurtTypeTeam:
+                    tagObj = self.__GetMaxHurtTeamPlayer(ownerID, isDead)
+                elif ownerType == ChConfig.Def_NPCHurtTypePlayer:
+                    tagObj = GameWorld.GetObj(ownerID, IPY_GameWorld.gotPlayer)
+                    
+        elif dropOwnerType == ChConfig.DropOwnerType_Family:
+            ownerInfo = FamilyRobBoss.RefreshFamilyOwnerNPCHurt(curNPC, tick, refreshInterval)
+            if ownerInfo:
+                tagObj, ownerFamilyID = ownerInfo
+                ownerType, ownerID = ChConfig.Def_NPCHurtTypeFamily, ownerFamilyID
+                
+        elif dropOwnerType == ChConfig.DropOwnerType_Contend:
+            tagObj = self.__RefreshContendOwner()
+            if tagObj:
+                ownerType, ownerID = ChConfig.Def_NPCHurtTypePlayer, tagObj.GetPlayerID()
+                
+        if isDead:
+            GameWorld.Log("ownerType=%s, ownerID=%s, tagObjID=%s" % (ownerType, ownerID, 0 if not tagObj else tagObj.GetPlayerID()))
+                
+        # 没有攻击目标,则刷新仇恨,支持主动怪
+        if not tagObj:
+            angryObjType, maxAngryObj = None, None
+            self.RefreshAngryList(tick, refreshInterval, isUpdAngry=True)
+            maxAngry = self.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 and not ownerType:
+                if dropOwnerType == ChConfig.DropOwnerType_Contend:
+                    ownerType, ownerID = ChConfig.Def_NPCHurtTypePlayer, maxAngryObj.GetPlayerID()
+                elif maxAngryObj.GetTeamID():
+                    ownerType, ownerID = ChConfig.Def_NPCHurtTypeTeam, maxAngryObj.GetTeamID()
+                else:
+                    ownerType, ownerID = ChConfig.Def_NPCHurtTypePlayer, maxAngryObj.GetPlayerID()
+            
+            if isDead:
+                GameWorld.Log("angryObj, ownerType=%s, ownerID=%s" % (ownerType, ownerID))
+                
+        self.UpdateDropOwner(tick, ownerType, ownerID, isDead)
+        return tagObj
+    
+    def __RefreshContendOwner(self):
+        ## 刷新boss争夺归属者,归属移除时不做刷新新归属,默认由后面的仇恨刷新
+        
+        curNPC = self.__Instance
+        ownerID = curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_LastDropOwnerID)
+        ownerType = curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_LastDropOwnerType)
+        if not ownerID or ownerType != ChConfig.Def_NPCHurtTypePlayer:
+            return
+        
+        owner = GameWorld.GetObj(ownerID, IPY_GameWorld.gotPlayer)
+        if not owner:
+            return
+        
+        if not owner.GetVisible():
+            GameWorld.DebugLog("竞争归属玩家不可见,移除归属!playerID=%s" % ownerID)
+            return
+        
+        if owner.GetHP() <= 0 or owner.GetPlayerAction() == IPY_GameWorld.paDie:
+            GameWorld.DebugLog("竞争归属玩家死亡,移除归属!playerID=%s" % ownerID)
+            return
+        
+        refreshPoint = curNPC.GetRefreshPosAt(curNPC.GetCurRefreshPointIndex())
+        if not self.GetIsInRefreshPoint(owner.GetPosX(), owner.GetPosY(), refreshPoint):
+            GameWorld.DebugLog("竞争归属玩家不在boss范围里,移除归属!playerID=%s" % ownerID)
+            return
+        
+        GameWorld.DebugLog("竞争归属玩家归属正常!playerID=%s" % ownerID)
+        return owner
+
+    def __GetMaxHurtTeamPlayer(self, teamID, isDead):
+        ## 获取最大伤血队伍中攻击的目标队员
+        
+        curNPC = self.__Instance
+        curTeam = GameWorld.GetTeamManager().FindTeam(teamID)
+        if curTeam:
+            refreshPoint = curNPC.GetRefreshPosAt(curNPC.GetCurRefreshPointIndex())
+            if isDead:
+                GameWorld.Log("队伍成员数: teamID=%s,memberCount=%s" % (teamID, curTeam.GetMemberCount()))
+            for i in xrange(curTeam.GetMemberCount()):
+                curTeamPlayer = curTeam.GetMember(i)
+                if curTeamPlayer == None or curTeamPlayer.GetPlayerID() == 0:
+                    if isDead:
+                        GameWorld.Log("    i=%s, 队员为空!" % i)
+                    continue
+                if curTeamPlayer.GetHP() <= 0:
+                    if isDead:
+                        GameWorld.Log("    i=%s, 队员血量为0!, memPlayerID=%s" % (i, curTeamPlayer.GetPlayerID()))
+                    continue
+                if not curTeamPlayer.GetVisible():
+                    if isDead:
+                        GameWorld.Log("    i=%s, 队员不可见!, memPlayerID=%s" % (i, curTeamPlayer.GetPlayerID()))
+                    continue
+                if isDead:
+                    GameWorld.Log("    i=%s, 队员坐标(%s, %s)! memPlayerID=%s" % (i, curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), curTeamPlayer.GetPlayerID()))
+                if self.GetIsInRefreshPoint(curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), refreshPoint):
+                    return curTeamPlayer
+        else:
+            GameWorld.ErrLog("找不到该队伍: teamID=%s" % teamID)
+        return
+    
+    def UpdateDropOwner(self, tick, ownerType=0, ownerID=0, isDead=False):
+        
+        curNPC = self.__Instance
+        npcID = curNPC.GetNPCID()
+        dropOwnerType = GetDropOwnerType(curNPC)
+        if dropOwnerType not in [ChConfig.DropOwnerType_MaxHurt, ChConfig.DropOwnerType_MaxAngry, ChConfig.DropOwnerType_Family, ChConfig.DropOwnerType_Contend]:
+            #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)
+        
+        key = (GameWorld.GetGameWorld().GetLineID(), curNPC.GetID(), npcID)
+        if lastDropOwnerID and (lastDropOwnerType != ownerType or lastDropOwnerID != ownerID):
+            GameWorld.Log("归属变更, 清除旧归属! key=%s,ownerType=%s,ownerID=%s,lastDropOwnerType=%s,lastDropOwnerID=%s" 
+                          % (key, ownerType, ownerID, lastDropOwnerType, lastDropOwnerID))
+            self.__DelDropOwnerBuff(dropOwnerType, 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 isDead:
+            GameWorld.Log("Boss归属: key=%s,ownerType=%s,ownerID=%s" % (key, ownerType, ownerID))
+            
+        # 刷新归属
+        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
+                self.__AddDropOwnerPlayerBuff(curPlayer, tick)
+                if dropOwnerType == ChConfig.DropOwnerType_Contend:
+                    curPlayer.SetDict(ChConfig.Def_PlayerKey_ContendNPCObjID, curNPC.GetID())
+                    
+        elif ownerType == ChConfig.Def_NPCHurtTypeTeam:
+            curTeam = GameWorld.GetTeamManager().FindTeam(ownerID)
+            if not curTeam:
+                return
+            
+            # 因为有击杀次数限制,所以不是所有的队员都可以获得归属,所以这里设置为特殊指定玩家掉落
+            hurtType, hurtID = ChConfig.Def_NPCHurtTypeSpecial, 0
+            if isDead:
+                GameWorld.Log("队伍成员数: %s" % (curTeam.GetMemberCount()))
+            refreshPoint = curNPC.GetRefreshPosAt(curNPC.GetCurRefreshPointIndex())
+            for i in xrange(curTeam.GetMemberCount()):
+                curTeamPlayer = curTeam.GetMember(i)
+                if curTeamPlayer == None or curTeamPlayer.GetPlayerID() == 0:
+                    if isDead:
+                        GameWorld.Log("    i=%s, 成员不存在!" % (i))
+                    continue
+                
+                if curTeamPlayer.GetCopyMapID() == GameWorld.GetGameWorld().GetCopyMapID() \
+                    and self.GetIsInRefreshPoint(curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), refreshPoint) \
+                    and AttackCommon.CheckKillNPCByCnt(curTeamPlayer, curNPC, False) and curTeamPlayer.GetVisible():
+                    self.__AddDropOwnerPlayerBuff(curTeamPlayer, tick)
+                    killerDict[curTeamPlayer.GetPlayerID()] = curTeamPlayer
+                    if isDead:
+                        GameWorld.Log("    i=%s, 成员有归属权! memPlayerID=%s,背包剩余空格=%s" 
+                                      % (i, curTeamPlayer.GetPlayerID(), ItemCommon.GetItemPackSpace(curTeamPlayer, IPY_GameWorld.rptItem)))
+                        
+                # 不同线、或者距离超出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:
+                        GameWorld.Log("    i=%s, 成员无归属权! memPlayerID=%s,copyMapID=%s,pos(%s,%s),CheckKillNPCByCnt=%s" 
+                                      % (i, curTeamPlayer.GetPlayerID(), curTeamPlayer.GetCopyMapID(), 
+                                         curTeamPlayer.GetPosX(), curTeamPlayer.GetPosY(), 
+                                         AttackCommon.CheckKillNPCByCnt(curTeamPlayer, curNPC, False)))
+                        
+        elif ownerType == ChConfig.Def_NPCHurtTypeFamily:
+            
+            hurtType, hurtID = ChConfig.Def_NPCHurtTypeFamily, ownerID
+            refreshPoint = curNPC.GetRefreshPosAt(curNPC.GetCurRefreshPointIndex())
+            copyPlayerMgr = GameWorld.GetMapCopyPlayerManager()
+            for index in xrange(copyPlayerMgr.GetPlayerCount()):
+                player = copyPlayerMgr.GetPlayerByIndex(index)
+                if not player:
+                    continue
+                
+                # 归属仙盟 且 在boss区域内
+                if player.GetFamilyID() == ownerID and self.GetIsInRefreshPoint(player.GetPosX(), player.GetPosY(), refreshPoint) and player.GetVisible():
+                    self.__AddDropOwnerPlayerBuff(player, tick)
+                    
+                else:
+                    isOk = BuffSkill.DelBuffBySkillID(player, ChConfig.Def_SkillID_DropOwnerBuff, tick, buffOwner=curNPC)
+                    if isOk:
+                        GameWorld.DebugLog("删除非归属仙盟成员buff: teamID=%s,playerID=%s" % (ownerID, player.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
+            elif ownerType == ChConfig.Def_NPCHurtTypeFamily:
+                PyGameData.g_npcKillerInfo[key] = {}, None, hurtType, hurtID
+                
+            GameWorld.Log("Boss被击杀: npcID=%s,key=%s,playerIDList=%s,teamID=%s,hurtType=%s,hurtID=%s" 
+                          % (npcID, key, killerDict.keys(), teamID, hurtType, hurtID))
+        return
+
+    def __AddDropOwnerPlayerBuff(self, curPlayer, tick):
+        curNPC = self.__Instance
+        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 __DelDropOwnerBuff(self, dropOwnerType, ownerType, ownerID, tick):
+        
+        curNPC = self.__Instance
+        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)
+            if dropOwnerType == ChConfig.DropOwnerType_Contend:
+                curPlayer.SetDict(ChConfig.Def_PlayerKey_ContendNPCObjID, 0)
+                
+        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 DelayDropOwnerBuffDisappearTime(self):
+        ''' 延迟掉落归属buff消失时间 '''
+        
+        curNPC = self.__Instance
+        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
+            self.__SetDropOwnerBuffDisappearTime(curPlayer)
+            
+        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
+                self.__SetDropOwnerBuffDisappearTime(curTeamPlayer)
+        elif ownerType == ChConfig.Def_NPCHurtTypeFamily:
+            copyPlayerMgr = GameWorld.GetMapCopyPlayerManager()
+            for index in xrange(copyPlayerMgr.GetPlayerCount()):
+                player = copyPlayerMgr.GetPlayerByIndex(index)
+                if not player:
+                    continue
+                self.__SetDropOwnerBuffDisappearTime(player)
+                
+        return
+    
+    def __SetDropOwnerBuffDisappearTime(self, curPlayer):
+        ''' 设置掉落归属buff消失时间 '''
+        
+        curNPC = self.__Instance
+        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
+    ##--------------------------------------------- -----------------------------------------------
+    
+def OnPlayerKillNPCPlayer(curPlayer, defender, tick):
+    ## 玩家击杀了NPC相关的玩家
+    contendNPCObjID = defender.GetDictByKey(ChConfig.Def_PlayerKey_ContendNPCObjID)
+    if contendNPCObjID:
+        curNPC = GameWorld.FindNPCByID(contendNPCObjID)
+        if not curNPC:
+            return
+        dropOwnerType = GetDropOwnerType(curNPC)
+        if dropOwnerType != ChConfig.DropOwnerType_Contend:
+            return
+        playerID = curPlayer.GetPlayerID()
+        GameWorld.DebugLog("玩家击杀竞争归属者! defPlayerID=%s,contendNPCObjID=%s,npcID=%s" 
+                           % (defender.GetPlayerID(), contendNPCObjID, curNPC.GetNPCID()), playerID)
+        npcControl = NPCControl(curNPC)
+        npcControl.UpdateDropOwner(tick, ChConfig.Def_NPCHurtTypePlayer, playerID, False)
+        
+    return
+
 #---------------------------------------------------------------------
 def SendVirtualItemDrop(player, itemID, posX, posY, userDataStr):
     #通知客户端假物品掉落
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCustomRefresh.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCustomRefresh.py
index 2493ede..cc68643 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCustomRefresh.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCustomRefresh.py
@@ -18,6 +18,7 @@
 import ReadChConfig
 import GameLogic_SealDemon
 import GameLogic_ZhuXianBoss
+import GameLogic_CrossDemonKing
 import PlayerControl
 import IPY_GameWorld
 import IpyGameDataPY
@@ -481,24 +482,34 @@
     
     refreshMark = npcRefresh.GetRefreshMark()
     lineID = GameWorld.GetGameWorld().GetLineID()
-    bossIpyData = IpyGameDataPY.GetIpyGameDataByCondition('BOSSInfo', {'RefreshMark':refreshMark, 'MapID':mapID}, isLogNone=False)
-    if not bossIpyData:
-        return
-    
-    stoneNPCID = bossIpyData.GetStoneNPCID()
-    bossID = bossIpyData.GetNPCID()
-    if not bossID and not stoneNPCID:
-        return
-    
-    if mapID not in ChConfig.Def_CrossZoneMapTableName:
-        return
-    tableName = ChConfig.Def_CrossZoneMapTableName[mapID]
     realMapID = GameWorld.GetGameWorld().GetRealMapID()
     copyMapID = GameWorld.GetGameWorld().GetCopyMapID()
-    zoneIpyData = IpyGameDataPY.GetIpyGameDataNotLog(tableName, realMapID, mapID, copyMapID)
-    if not zoneIpyData:
+    
+    if mapID == ChConfig.Def_FBMapID_CrossDemonKing:
+        bossID = GameLogic_CrossDemonKing.GetCurFBLineBOSSID(lineID)
+        stoneNPCID = 0
+        zoneID = GameLogic_CrossDemonKing.GetCurFBLineZoneID()
+        
+    else:
+        bossIpyData = IpyGameDataPY.GetIpyGameDataByCondition('BOSSInfo', {'RefreshMark':refreshMark, 'MapID':mapID}, isLogNone=False)
+        if not bossIpyData:
+            return
+        
+        stoneNPCID = bossIpyData.GetStoneNPCID()
+        bossID = bossIpyData.GetNPCID()
+
+        if mapID not in ChConfig.Def_CrossZoneMapTableName:
+            return
+        tableName = ChConfig.Def_CrossZoneMapTableName[mapID]
+        zoneIpyData = IpyGameDataPY.GetIpyGameDataNotLog(tableName, realMapID, mapID, copyMapID)
+        if not zoneIpyData:
+            return
+        zoneID = zoneIpyData.GetZoneID()
+        
+    if not zoneID:
         return
-    zoneID = zoneIpyData.GetZoneID()
+    if not bossID and not stoneNPCID:
+        return
     
     gameFB = GameWorld.GetGameFB()
     bosskey = ShareDefine.Def_Notify_WorldKey_GameWorldBossRebornCross % (zoneID, bossID)
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 29529b2..03288e3 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
@@ -4512,10 +4512,11 @@
 #{
 #    tagHead        Head;
 #    DWORD        DataMapID;
+#    WORD        LineID;
 #};
 def OnEnterCrossServer(index, clientData, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    PlayerControl.PlayerEnterCrossServer(curPlayer, clientData.DataMapID)
+    PlayerControl.PlayerEnterCrossServer(curPlayer, clientData.DataMapID, clientData.LineID)
     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 faa901d..d122e9c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -74,6 +74,7 @@
 import PlayerMagicWeapon
 import GameLogic_SealDemon
 import GameLogic_ZhuXianBoss
+import GameLogic_CrossDemonKing
 import PlayerTJG
 import PlayerVip
 import PlayerRefineStove
@@ -1586,9 +1587,12 @@
 
 #---------------------------------------------------------------------
 
-def PlayerEnterCrossServer(curPlayer, mapID):
+def PlayerEnterCrossServer(curPlayer, mapID, lineID):
     playerID = curPlayer.GetPlayerID()
-    GameWorld.Log("玩家请求进入跨服地图: mapID=%s" % (mapID), playerID)
+    GameWorld.Log("玩家请求进入跨服地图: mapID=%s,lineID=%s" % (mapID, lineID), playerID)
+    if mapID not in ChConfig.Def_CrossMapIDList:
+        return
+    
     if GameWorld.IsCrossServer():
         GameWorld.DebugLog("跨服服务器不允许该操作!")
         return
@@ -1613,6 +1617,21 @@
         NotifyCode(curPlayer, "SingleEnterPK", [mapID])
         return
     
+    # 需要动态分布线路的地图,发送到跨服服务器进行分配
+    if mapID in ChConfig.Def_CrossDynamicLineMap:
+        extendInfo = {}
+        if mapID == ChConfig.Def_FBMapID_CrossDemonKing:
+            bossID = GameLogic_CrossDemonKing.GetCurFBLineBOSSID(lineID)
+            if not bossID:
+                return
+            extendInfo["BossID"] = bossID
+            
+        msgDict = {"PlayerID":curPlayer.GetPlayerID(), "DataMapID":mapID, "FuncLineID":lineID}
+        if extendInfo:
+            msgDict.update(extendInfo)
+        GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_EnterFB, msgDict)
+        return
+    
     GY_Query_CrossRealmReg.RegisterEnterCrossServer(curPlayer, mapID)
     return
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_OpenFB.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_OpenFB.py
new file mode 100644
index 0000000..85e2209
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_OpenFB.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package Player.RemoteQuery.GY_Query_OpenFB
+#
+# @todo:开启副本线路
+# @author hxp
+# @date 2019-04-11
+# @version 1.0
+#
+# 详细描述: 开启副本线路
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2019-04-11 14:30"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import IPY_GameWorld
+
+
+#逻辑实现
+## 请求逻辑
+#  @param query_Type 请求类型
+#  @param query_ID 请求的玩家ID
+#  @param paramList 发包命令
+#  @param tick 当前时间
+#  @return "True" or "False" or ""
+#  @remarks 函数详细说明.
+def DoLogic(query_Type, query_ID, paramList, tick):
+    copyMapID, propertyID = paramList
+    
+    gameWorldManager = GameWorld.GetGameWorld()
+    maxCopyCount = gameWorldManager.GetGameWorldCount()
+    if copyMapID >= maxCopyCount:
+        GameWorld.ErrLog("GY_Query_OpenFB 虚拟分线不存在! copyMapID=%s, propertyID=%s, maxCopyCount=%s" 
+                         % (copyMapID, propertyID, maxCopyCount))
+        return
+    
+    tagGameWorld = IPY_GameWorld.IPY_GameWorld(copyMapID)
+    if tagGameWorld.GetOpenState() != IPY_GameWorld.fbosClosed:
+        GameWorld.ErrLog("GY_Query_OpenFB 虚拟分线已经是开启状态! copyMapID=%s, propertyID=%s,  GetPropertyID=%s" 
+                         % (copyMapID, propertyID, tagGameWorld.GetPropertyID()))
+        return
+    
+    if tagGameWorld.GetFBFirstOpen():
+        GameWorld.ErrLog("GY_Query_OpenFB 虚拟分线已经在开启中! copyMapID=%s, propertyID=%s,  GetPropertyID=%s" 
+                         % (copyMapID, propertyID, tagGameWorld.GetPropertyID()))
+        return
+    
+    GameWorld.Log("GY_Query_OpenFB copyMapID=%s, propertyID=%s" % (copyMapID, propertyID))
+    tagGameWorld.SetFBFirstOpen(1) # 开启副本
+    tagGameWorld.SetPropertyID(propertyID)
+    return ''
+
+                
+#---------------------------------------------------------------------
+#执行结果
+## 执行结果
+#  @param curPlayer 发出请求的玩家
+#  @param callFunName 功能名称
+#  @param funResult 查询的结果
+#  @param tick 当前时间
+#  @return None
+#  @remarks 函数详细说明.
+def DoResult(curPlayer, callFunName, funResult, tick):
+    return
+
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
index c2d09f5..063ab36 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
@@ -1194,6 +1194,7 @@
 CrossServerMsg_RebornRet = "RebornRet"                  # 复活结果
 CrossServerMsg_NPCInfoRet = "NPCInfoRet"                # 跨服地图NPC信息
 CrossServerMsg_CollectNPCOK = "CollectNPCOK"            # 采集NPC完成
+CrossServerMsg_EnterFBRet = "EnterFBRet"                # 请求进入跨服副本返回信息
 
 # 子服发送跨服信息定义
 ClientServerMsg_ServerInitOK = "ServerInitOK"           # 子服启动成功
@@ -1208,6 +1209,7 @@
 ClientServerMsg_QueryNPCInfo = "QueryNPCInfo"           # 查询跨服地图NPC信息
 ClientServerMsg_SetPlayerAttrValue = "SetPlayerAttrValue" # 玩家属性数值更新
 ClientServerMsg_CollectNPC = "CollectNPC"               # 采集NPC
+ClientServerMsg_EnterFB = "EnterFB"                     # 请求进入跨服副本
 
 #跨服广播类型定义
 CrossNotify_World = "World"

--
Gitblit v1.8.0