6459 【后端】【2.0】缥缈仙域开发单(可进入跨服妖王地图支持分区,增加竞争归属逻辑)
21个文件已修改
2个文件已添加
1552 ■■■■ 已修改文件
ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBoss.py 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/EnterFB.py 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossDemonKing.py 417 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCAI/AIType_186.py 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py 348 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCustomRefresh.py 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_OpenFB.py 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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
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
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:
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)
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()
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: 请求回调名
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)
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, ...}, ...}, ...}
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"
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
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" # 属性激活记录 - 强化星级
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
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
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
    
    #玩家 在副本中,并且副本不踢出玩家下线
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossDemonKing.py
New file
@@ -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
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
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攻击逻辑
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):
    #通知客户端假物品掉落
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)
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
    
#===============================================================================
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
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_OpenFB.py
New file
@@ -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
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"