10263 【越南】【英文】【BT】后端支持NPC仿真实玩家战斗和快速战斗(改为真实地图战斗;竞技场、跨服PK、跨服排位赛,测试地图100均已支持;)
16个文件已修改
1033 ■■■■ 已修改文件
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py 422 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py 87 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_ArenaBattle.py 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossChampionship.py 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossRealmPK.py 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py 183 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerArena.py 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_EnterFB.py 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
@@ -34,6 +34,7 @@
import CrossBattlefield
import CrossRealmPlayer
import CrossChampionship
import PlayerPackData
import DataRecordPack
import CrossRealmMsg
import PyDataManager
@@ -754,7 +755,7 @@
#  @return None
def EnterFBLine(curPlayer, queryCallName, sendCMD, tick):
    playerID = curPlayer.GetPlayerID()
    GameWorld.Log("EnterFBLine()...queryCallName=%s,sendCMD=%s" % (queryCallName, sendCMD), curPlayer.GetPlayerID())
    GameWorld.Log("EnterFBLine()...queryCallName=%s,sendCMD=%s" % (queryCallName, sendCMD), playerID)
    playerManager = GameWorld.GetPlayerManager()
    try:
        mapInfo = eval(sendCMD)
@@ -769,6 +770,7 @@
    #if mapInfo and len(mapInfo) == 2:
    tagMapID = mapInfo[0]
    tagLineID = mapInfo[1]
    extendValue1 = mapInfo[2] if len(mapInfo) > 2 else None
    
    fbLineIpyData = GetFBLineIpyData(tagMapID, tagLineID)
    sceneMapID = tagMapID if not fbLineIpyData else fbLineIpyData.GetMapID()
@@ -777,6 +779,13 @@
        GameWorld.ErrLog("目标副本地图不存在!tagMapID=%s,sceneMapID=%s" % (tagMapID, sceneMapID), curPlayer.GetPlayerID())
        return
    
    if isinstance(extendValue1, dict):
        if extendValue1.get("msgType", "") == "MirrorBattle":
            PlayerPackData.OnMGReuestPlayerPackData(extendValue1)
            playerManager.MapServer_QueryPlayer(curPlayer.GetPlayerID(), ChConfig.queryType_EnterFB, 0, sceneMapID,
                                                queryCallName, sendCMD, len(sendCMD), curPlayer.GetRouteServerIndex())
        return
    # 组队副本, 有队伍的情况才验证其他队员可否进入,否则代表单人进入
    if gameMap.GetMapFBType() == ChConfig.fbtTeam:
        if tagMapID == ChConfig.Def_FBMapID_Love:
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py
@@ -408,22 +408,23 @@
    msgType = msgInfo.get("msgType")
    # 镜像战斗
    if msgType == "MirrorBattle":
        sceneMapID = msgInfo.get("sceneMapID", 0)
        playerID = msgInfo.get("playerID", 0)
        # 玩家发起的
        if playerID:
            playerID = msgInfo["playerID"]
            curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
            if not curPlayer:
                return
            tagMapID = GameWorld.GetQueryPlayerMapID(curPlayer)
            routeIndex = curPlayer.GetRouteServerIndex()
        else:
            tagMapID = msgInfo.get("requestMapID", 0)
            routeIndex = -1
        routeIndex = -1
#        # 玩家发起的
#        if playerID:
#            playerID = msgInfo["playerID"]
#            curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
#            if not curPlayer:
#                return
#            sceneMapID = GameWorld.GetQueryPlayerMapID(curPlayer)
#            routeIndex = curPlayer.GetRouteServerIndex()
#        else:
#            routeIndex = -1
            
        sendMsg = str([msgInfo, packDataDict])
        GameWorld.DebugLog("MapServer_QueryPlayer tagMapID=%s,len=%s" % (tagMapID, len(sendMsg)), playerID)
        GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, 0, playerID, tagMapID, "PlayerMirror", sendMsg, len(sendMsg), routeIndex)
        GameWorld.DebugLog("MapServer_QueryPlayer sceneMapID=%s,len=%s" % (sceneMapID, len(sendMsg)), playerID)
        GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, 0, playerID, sceneMapID, "PlayerMirror", sendMsg, len(sendMsg), routeIndex)
        
    # 其他功能可再扩展
    else:
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py
@@ -29,6 +29,7 @@
import OperControlManager
import PlayerViewCacheTube
import PassiveBuffEffMng
import GameWorldProcess
import ChNetSendPack
import IpyGameDataPY
import AttackCommon
@@ -43,6 +44,8 @@
import time
Def_StateTimeList = [3, 60, 10] # 默认阶段时长,秒
class MirrorBattle():
    ## 某场战斗
    
@@ -51,6 +54,7 @@
        return
    
    def Clear(self):
        self.isChangeMap = 0 # 是否切图战斗的
        self.isSysbg = False # 是否系统后台进行战斗的,玩家无感知,仅知道结果
        self.requestID = 0 # 请求ID,一般是玩家ID或者系统自定的ID,如某一场PK的标识信息
        self.playerID = 0 # 所属玩家ID,可能为0
@@ -58,9 +62,8 @@
        self.mapID = 0 # 功能mapID,代表某一个功能
        self.funcLineID = 0
        self.batState = 0 # 状态:0-无;1-准备中;2-战斗中;3-快速结束中,4-结束
        self.startTick = 0 # 开始战斗tick
        self.fightTickMax = 0 # 战斗最大时长,tick
        self.fightTickRemain = 0 # 剩余战斗时长,tick
        self.stateTick = 0 # 阶段状态变更时tick
        self.stateTickRemain = 0 # 阶段剩余时长,tick
        self.mirrorIDDict = {} # 该场所有玩家镜像实例ID对应真实ID {playerID:realPlayerID, ...}
        self.realIDDict = {} # 该场所有真实玩家对应初始信息 {playerID:{k:v, ...}, ...}
        self.playerFactionDict = {} # 该场所有玩家阵营信息,真实玩家+镜像玩家 {playerID:faction, ...}
@@ -72,15 +75,79 @@
        self.isQuick = False # 是否快速战斗结束的
        self.isWin = False # 是否获胜
        self.winFaction = 0 # 获胜阵营
        # 结算时血量明细
        self.curHP = 0
        self.curHPMax = 0
        self.tagHP = 0
        self.tagHPMax = 0
        return
    
    def GetTagPlayerID(self): return self.tagPlayerIDList[0] if self.tagPlayerIDList else 0
    
    def CaclFightTick(self, tick):
        ## 计算战斗时长,返回剩余时长tick
        fightTickCost = tick - self.startTick # 已过战斗时长
        self.fightTickRemain = max(0, self.fightTickMax - fightTickCost) # 剩余战斗时长
        return self.fightTickRemain
    def SyncFBStepTime(self, tick):
        #curPlayer.Sync_TimeTick(IPY_GameWorld.tttWaitStart, 0, max(notify_tick, 0), True)
        if self.isSysbg:
            return
        self.CaclStateTick(tick)
        state = self.batState
        if state == ChConfig.Def_MirrorBatState_Prepare:
            self.__SyncRealPlayerTick(ChConfig.tttWaitStart, self.stateTickRemain)
        elif state == ChConfig.Def_MirrorBatState_Fight:
            self.__SyncRealPlayerTick(ChConfig.tttTowerTake, self.stateTickRemain)
        elif state == ChConfig.Def_MirrorBatState_Over:
            self.__SyncRealPlayerTick(ChConfig.tttLeaveMap, self.stateTickRemain)
        return
    def __SyncRealPlayerTick(self, msgType, msgTick):
        playerMgr = GameWorld.GetMapCopyPlayerManager()
        for playerID in self.realIDDict.keys():
            curPlayer = playerMgr.FindPlayerByID(playerID)
            if not curPlayer:
                continue
            curPlayer.Sync_TimeTick(msgType, 0, msgTick, True)
        return
    def ChangeBattleState(self, state, tick):
        GameWorld.DebugLog("镜像战斗阶段变更: mapID=%s,state=%s" % (self.mapID, state), self.battleID)
        self.batState = state
        self.stateTick = tick
        self.stateTickRemain = self.GetStateTickMax()
        self.SyncFBStepTime(tick)
        return
    def CaclStateTick(self, tick):
        ## 计算状态时长,返回剩余时长tick
        stateTickMax = self.GetStateTickMax()
        passTick = tick - self.stateTick # 已过时长
        self.stateTickRemain = max(0, stateTickMax - passTick) # 剩余时长
        return self.stateTickRemain
    def GetStateTickRemain(self): return self.stateTickRemain
    def GetStateTickMax(self):
        fightTimeLimitDict = IpyGameDataPY.GetFuncEvalCfg("MirrorAttack", 1, {})
        stateTimeList = fightTimeLimitDict.get(self.mapID, []) # 阶段时长列表
        if not stateTimeList or len(stateTimeList) != 3:
            stateTimeList = Def_StateTimeList
        state = self.batState
        stateTime = 0
        if state == ChConfig.Def_MirrorBatState_Prepare:
            stateTime = stateTimeList[0]
        elif state == ChConfig.Def_MirrorBatState_Fight:
            stateTime = stateTimeList[1]
        elif state == ChConfig.Def_MirrorBatState_Over:
            stateTime = stateTimeList[2]
        return stateTime * 1000
    def CalcHPPer(self):
        ## 结算当前阶段双方阵营剩余血量占比,一般用于结算计算
        curHPPer = round(self.curHP / float(self.curHPMax) * 100, 2)
        tagHPPer = round(self.tagHP / float(self.tagHPMax) * 100, 2)
        return curHPPer, tagHPPer
    def CalcRemainTimePer(self):
        ## 结算当前阶段剩余时间占比,一般用于结算计算
        remainTimePer = round(self.stateTickRemain / float(self.GetStateTickMax()) * 100)
        return remainTimePer
    
    def AddBattlePlayer(self, curPlayer, faction, posX=0, posY=0):
        playerID = curPlayer.GetPlayerID()
@@ -88,7 +155,7 @@
        if realPlayerID:
            self.mirrorIDDict[playerID] = realPlayerID
        else:
            self.realIDDict[playerID] = {"SightLevel":curPlayer.GetSightLevel(), "Faction":curPlayer.GetFaction(), "PosX":curPlayer.GetPosX(), "PosY":curPlayer.GetPosY()}
            self.realIDDict[playerID] = {"SightLevel":curPlayer.GetSightLevel(), "Faction":curPlayer.GetFaction()}
        self.playerFactionDict[playerID] = faction
                
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleID, self.battleID)
@@ -97,6 +164,7 @@
        curPlayer.SetCanAttack(True)
        curPlayer.SetFaction(faction)
        PlayerControl.SetPlayerSightLevel(curPlayer, self.battleID) # 视野层级默认为战场ID,每场战斗的玩家独立视野
        PlayerControl.SetSight(curPlayer, ChConfig.Def_PlayerSight_Default * 3)
        GameObj.SetHPFull(curPlayer) # 回满血
        SkillCommon.ResetAllSkillCD(curPlayer) # 重置技能CD
        return
@@ -144,7 +212,9 @@
def GetMirrorBattle(curPlayer):
    ## 获取玩家实例所属的战场
    return GetMirrorBattleByID(curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_MirrorBattleID))
    if curPlayer.GetRealPlayerID():
        return GetMirrorBattleByID(curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_MirrorBattleID))
    return GetMirrorBattleByID(curPlayer.GetPlayerID())
def GetMirrorBattleByID(battleID):
    battle = None
@@ -155,17 +225,14 @@
        battle = MirrorBattle()
    return battle
def ClearMirrorBattleByPlayer(curPlayer):
    ## 清除玩家创建的镜像战斗
    ClearMirrorBattleByID(curPlayer.GetPlayerID())
    return
def ClearMirrorBattleByID(battleID):
    ## 清除镜像战斗
    battle = PyGameData.g_mirrorBattleDict.pop(battleID, None)
    if not battle:
        return
    ownerPlayerID = battle.playerID
    isSysbg = battle.isSysbg
    GameWorld.DebugLog("清除镜像战斗: battleID=%s,ownerPlayerID=%s,isSysbg=%s" % (isSysbg, ownerPlayerID, isSysbg), battleID)
    playerMgr = GameWorld.GetPlayerManager()
    tick = GameWorld.GetGameWorld().GetTick()
    
@@ -176,30 +243,25 @@
            PlayerControl.DeleteMirror(mirrorPlayer, isSysbg) # 系统场延迟回收
            
    # 重置真实玩家
    for playerID, info in battle.realIDDict.items():
        curPlayer = playerMgr.FindPlayerByID(playerID)
    for realPlayerID, info in battle.realIDDict.items():
        curPlayer = playerMgr.FindPlayerByID(realPlayerID)
        if not curPlayer:
            continue
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleID, 0)
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleTime, 0)
        if curPlayer.GetPlayerAction() == IPY_GameWorld.paDie or GameObj.GetHP(curPlayer) <= 0:
            ChPlayer.PlayerRebornByType(curPlayer, ChConfig.rebornType_System, tick, isAddSuperBuff=False)
        if "PosX" in info:
            curPlayer.ResetPos(info["PosX"], info["PosY"]) # 回到进去前的坐标
        curPlayer.SetFaction(0)
        PlayerControl.SetPlayerSightLevel(curPlayer, 0)
        curPlayer.SetFaction(info.get("Faction", 0))
        PlayerControl.SetPlayerSightLevel(curPlayer, info.get("SightLevel", 0))
        PlayerControl.SetSight(curPlayer, ChConfig.Def_PlayerSight_Default)
        GameObj.SetHPFull(curPlayer) # 回满血
        SkillCommon.ResetAllSkillCD(curPlayer) # 重置技能CD
        curPlayer.SetAttackTick(tick)
        ChPlayer.__Sync_ClientBuff(curPlayer)
        
    # 所属玩家
    playerID = battle.playerID
    curPlayer = playerMgr.FindPlayerByID(playerID)
    if curPlayer:
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleID, 0)
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleTime, 0)
    # 如果是真实地图战斗的,关闭副本
    if battle.isChangeMap:
        GameWorldProcess.CloseFB(GameWorld.GetGameWorld().GetTick())
    return
def CreateMirrorPlayer(battleID, mirrorPlayerID, mirrorPlayerData, posX=0, posY=0, faction=0, curPlayer=None):
@@ -227,7 +289,10 @@
        GameWorld.ErrLog("CreateMirrorPlayer mirrorPlayerID=%s,posX=%s,posY=%s,faction=%s" 
                         % (mirrorPlayerID, posX, posY, faction), playerID)
        return
    PlayerControl.SetCustomMap(mirrorPlayer, mapID, funcLineID)
    if not battle.isChangeMap:
        PlayerControl.SetCustomMap(mirrorPlayer, mapID, funcLineID)
    else:
        PlayerControl.SetCustomMap(mirrorPlayer, 0, 0)
    mirrorID = mirrorPlayer.GetID()
    realPlayerID = mirrorPlayer.GetRealPlayerID()
    dataFightPower = PlayerControl.GetFightPower(mirrorPlayer)
@@ -289,8 +354,11 @@
    realPlayerID = curPlayer.GetRealPlayerID()
    posX = curPlayer.GetPosX()
    posY = curPlayer.GetPosY()
    faction = curPlayer.GetFaction()
    sightLevel = curPlayer.GetSightLevel()
    GameWorld.DebugLog("realPlayerID=%s,posX=%s,posY=%s,sightLevel=%s" % (realPlayerID, posX, posY, sightLevel), playerID)
    sight = curPlayer.GetSight()
    GameWorld.DebugLog("realPlayerID=%s,posX=%s,posY=%s,faction=%s,sightLevel=%s,sight=%s"
                       % (realPlayerID, posX, posY, faction, sightLevel, sight), playerID)
    GameWorld.DebugLog("生命=%s/%s, 护盾=%s/%s" 
                       % (GameObj.GetHP(curPlayer), GameObj.GetMaxHP(curPlayer), 
                          PlayerControl.GetProDef(curPlayer), PlayerControl.GetMaxProDef(curPlayer)), playerID)
@@ -394,21 +462,15 @@
    
    # 创建战斗,玩家自身参与
    if cmdType == 0:
        isSysbg = False
        requestID = playerID
        posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
        battlePlayerList = []
        battlePlayerList.append({"playerID":playerID, "faction":1, "posX":posX, "posY":posY})
        battlePlayerList.append({"playerID":tagPlayeID, "faction":2, "posX":posX + 5, "posY":posY})
        OnRequestCreateMirrorBattle(mapID, funcLineID, requestID, battlePlayerList, isSysbg, curPlayer)
        OnRequestCreateMirrorBattle(mapID, funcLineID, playerID, [[playerID], [tagPlayeID]], False, curPlayer, True)
        return
    
    # 开始战斗
    if cmdType == 1:
        battle = GetMirrorBattle(curPlayer)
        if battle:
            OnMirrorBattleStart(battle.battleID)
        return
    # 开始战斗,该流程改为后端控制
    #if cmdType == 1:
    #    battle = GetMirrorBattle(curPlayer)
    #   if battle:
    #        OnMirrorBattleStart(battle.battleID)
    #    return
    
    # 战斗中跳过
    if cmdType == 2:
@@ -419,48 +481,29 @@
    
    # 不战斗直接跳过,即玩家没有参与,创建系统战斗场,之后扩展
    if cmdType == 3:
        isSysbg = True
        requestID = playerID
        posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
        battlePlayerList = []
        battlePlayerList.append({"playerID":playerID, "faction":1, "posX":posX, "posY":posY})
        battlePlayerList.append({"playerID":tagPlayeID, "faction":2, "posX":posX + 5, "posY":posY})
        OnRequestCreateMirrorBattle(mapID, funcLineID, requestID, battlePlayerList, isSysbg, curPlayer)
        OnRequestCreateMirrorBattle(mapID, funcLineID, playerID, [[playerID], [tagPlayeID]], True, curPlayer)
        return
    
    # 可不做验证,PK结束后由各个功能自行做结算验证
    return
def OnPlayerLeaveMap(curPlayer):
    ## 玩家离开地图
    if curPlayer.GetRealPlayerID():
        return
    battle = GetMirrorBattle(curPlayer)
    if not battle:
        return
    # 如果还在战斗中,直接快速执行战斗结果
    if battle.batState == ChConfig.Def_MirrorBatState_Fight:
        DoMirrorBattleQuick(battle.battleID, isLogout=True)
    # 统一退出
    if PlayerControl.GetCustomMapID(curPlayer):
        PlayerFB.DoExitCustomScene(curPlayer)
    return
def OnRequestCreateMirrorBattle(mapID, funcLineID, requestID, battlePlayerList, isSysbg=False, curPlayer=None):
def OnRequestCreateMirrorBattle(mapID, funcLineID, requestID, factionPlayerList, isSysbg=False, curPlayer=None, isChangeMap=False):
    ''' 请求创建镜像战斗,支持多对多,支持跨服,本服跨服地图中均可直接请求
    @param mapID: 功能地图ID
    @param funcLineID: 功能地图线路ID
    @param requestID: 请求ID,如果是玩家发起的,一般传入玩家ID;如果是系统发起的,由系统自行决定,比如roomID之类
    @param battlePlayerList: 战斗的玩家信息列表 [{"playerID":玩家ID, "posX":坐标x, "posY":坐标y, "faction":阵营}, ...]
    @param factionPlayerList: 战斗的阵营玩家列表 [[阵营1玩家ID, ...], [阵营2玩家ID, ...]]
    @param isSysbg: 是否后台战斗,默认否,但是系统发起的默认是
    @param curPlayer: 发起的玩家,为空时代表系统发起创建的
    @param isChangeMap: 是否切战斗场景地图
    '''
    
    playerID = 0
    if curPlayer:
        if not FBLogic.OnMirrorBattleRequest(curPlayer, mapID, funcLineID):
        if GameWorld.IsCrossServer():
            GameWorld.DebugLog("跨服服务器中不允许玩家镜像战斗请求!", playerID)
            return
        if not FBLogic.OnMirrorBattleRequest(curPlayer, mapID, funcLineID, factionPlayerList):
            GameWorld.DebugLog("当前不允许该镜像战斗请求! mapID=%s,funcLineID=%s" % (mapID, funcLineID), playerID)
            return
        playerID = curPlayer.GetPlayerID()
@@ -477,27 +520,56 @@
    else:
        isSysbg = True # 系统发起的默认后台战斗
        
    mirrorIDList = []
    for battleInfo in battlePlayerList:
        batPlayerID = battleInfo["playerID"]
        faction = battleInfo["faction"]
        if batPlayerID == playerID and faction == 1 and not isSysbg:
            # 自己不用,使用自身进行战斗即可
            continue
        if batPlayerID not in mirrorIDList:
            mirrorIDList.append(batPlayerID)
    mirrorIDList = [] # 需要使用镜像的玩家ID列表
    for faction, batPlayerIDList in enumerate(factionPlayerList, 1):
        for batPlayerID in batPlayerIDList:
            if batPlayerID == playerID and faction == 1 and not isSysbg:
                # 自己不用,使用自身进行战斗即可
                continue
            if batPlayerID not in mirrorIDList:
                mirrorIDList.append(batPlayerID)
    # 战斗相关的数据
    msgData = {"mapID":mapID, "funcLineID":funcLineID, "battlePlayerList":battlePlayerList, "isSysbg":isSysbg}
    msgData = {"mapID":mapID, "funcLineID":funcLineID, "factionPlayerList":factionPlayerList, "isSysbg":isSysbg}
    
    # 发送GameServer请求玩家打包数据
    requestTime = curTime # 请求的时间戳,每个玩家最多允许同时存在一场战斗,每次重新请求后覆盖数据
    requestMapID = GameWorld.GetGameWorld().GetRealMapID()
    sendMsg = str({"msgType":"MirrorBattle", "msgData":msgData, "mirrorIDList":mirrorIDList,
                   "requestTime":requestTime, "requestID":requestID, "requestMapID":requestMapID, "playerID":playerID})
    sceneMapID = GameWorld.GetGameWorld().GetRealMapID()
    sendMsg = {"msgType":"MirrorBattle", "msgData":msgData, "mirrorIDList":mirrorIDList,
               "requestTime":requestTime, "requestID":requestID, "sceneMapID":sceneMapID, "playerID":playerID}
    if isChangeMap:
        # 默认切到PK地图
        sendMsg["isChangeMap"] = 1
        PlayerControl.PlayerEnterFB(curPlayer, mapID, funcLineID, reqInfoEx=sendMsg)
        return True
    sendMsg = str(sendMsg)
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "ReuestPlayerPackData", sendMsg, len(sendMsg))
    GameWorld.DebugLog("请求创建镜像战斗: %s" % sendMsg, playerID)
    return True
def OnMirrorBattleEnterMapInit(curPlayer, tick):
    ''' 镜像战斗初始化  - 需要切图的,玩家切图成功或地图收到打包数据同步时会触发,即这两个条件都准备好后才开始初始化
    '''
    if not curPlayer or curPlayer.IsEmpty() or not curPlayer.GetMapLoadOK():
        return
    battle = GetMirrorBattle(curPlayer)
    if battle:
        GameWorld.DebugLog("镜像战斗玩家断线重连成功!", curPlayer.GetPlayerID())
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleID, battle.battleID)
        curPlayer.SetFaction(1)
        PlayerControl.SetPlayerSightLevel(curPlayer, battle.battleID)
        PlayerControl.SetSight(curPlayer, ChConfig.Def_PlayerSight_Default * 3)
        battle.SyncFBStepTime(tick)
        return
    playerID = curPlayer.GetPlayerID()
    if playerID not in PyGameData.g_playerReqEnterFBEx:
        return
    msgInfo, packDataDict = PyGameData.g_playerReqEnterFBEx[playerID]
    OnMirrorBattleInit(msgInfo, packDataDict, curPlayer)
    return
def OnMirrorBattleInit(msgInfo, packDataDict, curPlayer=None):
    ''' 镜像战斗初始化 
@@ -512,7 +584,7 @@
    
    mapID = msgData.get("mapID", 0)
    funcLineID = msgData.get("funcLineID", 0)
    battlePlayerList = msgData.get("battlePlayerList", [])
    factionPlayerList = msgData.get("factionPlayerList", [])
    isSysbg = msgData.get("isSysbg", 0) # 系统后台战斗
    
    battleID = 0
@@ -529,28 +601,28 @@
    if not battle:
        GameWorld.ErrLog("镜像战场ID已存在! battleID=%s,msgInfo=%s" % (battleID, msgInfo), requestID)
        return
    battle.isChangeMap = msgInfo.get("isChangeMap", 0)
    GameWorld.DebugLog("镜像战斗初始化: msgData=%s,packIDList=%s" % (msgData, packDataDict.keys()), battleID)
    
    for battleInfo in battlePlayerList:
        batPlayerID = battleInfo["playerID"]
        posX = battleInfo.get("posX", 0)
        posY = battleInfo.get("posY", 0)
        faction = battleInfo.get("faction", 0)
        if curPlayer and batPlayerID == playerID and faction == 1 and not isSysbg:
            battle.AddBattlePlayer(curPlayer, faction, posX, posY)
            continue
        packData = packDataDict.get(batPlayerID)
        if not packData:
            GameWorld.ErrLog("初始化镜像战斗时没有玩家镜像数据! batPlayerID=%s" % batPlayerID, playerID)
            continue
        CreateMirrorPlayer(battleID, batPlayerID, packData, posX, posY, faction, curPlayer)
    battle.batState = ChConfig.Def_MirrorBatState_Prepare
    fightTimeLimitDict = IpyGameDataPY.GetFuncEvalCfg("MirrorAttack", 1, {})
    battle.fightTickMax = fightTimeLimitDict.get(str(mapID), 60) * 1000 # 最大战斗时长,默认60秒
    factionPosList = OnGetMirrorBattlePos(mapID, funcLineID, isSysbg)
    for faction, batPlayerIDList in enumerate(factionPlayerList, 1):
        posInfo = factionPosList[faction - 1]
        factionPosX, factionPosY = posInfo
        for index, batPlayerID in enumerate(batPlayerIDList):
            posX, posY = factionPosX, factionPosY + index * 5
            if curPlayer and batPlayerID == playerID and faction == 1 and not isSysbg:
                battle.AddBattlePlayer(curPlayer, faction, posX, posY)
                continue
            packData = packDataDict.get(batPlayerID)
            if not packData:
                GameWorld.ErrLog("初始化镜像战斗时没有玩家镜像数据! batPlayerID=%s" % batPlayerID, playerID)
                continue
            CreateMirrorPlayer(battleID, batPlayerID, packData, posX, posY, faction, curPlayer)
    tick = GameWorld.GetGameWorld().GetTick()
    battle.ChangeBattleState(ChConfig.Def_MirrorBatState_Prepare, tick)
    
    if not isSysbg:
        return
@@ -558,7 +630,18 @@
    # 系统场默认直接开始、快速战斗结束
    OnMirrorBattleStart(battleID)
    DoMirrorBattleQuick(battleID)
    ClearMirrorBattleByID(battleID)
    return
def OnGetMirrorBattlePos(mapID, lineID, isSysbg=False):
    if isSysbg:
        gameMap = GameWorld.GetMap()
        posX, posY = gameMap.GetRebornMapX(), gameMap.GetRebornMapY() # 系统战斗默认取当前地图的复活点
        factionPosList = [[posX, posY], [posX + 5, posY]]
    else:
        factionPosDict = IpyGameDataPY.GetFuncEvalCfg("MirrorAttack", 4, {})
        factionPosList = factionPosDict.get(mapID, [[10, 7], [40,37]])
    return factionPosList
def OnMirrorBattleStart(battleID):
    ## 镜像战斗开始
@@ -567,8 +650,8 @@
        return
    if battle.batState >= ChConfig.Def_MirrorBatState_Fight:
        return
    battle.batState = ChConfig.Def_MirrorBatState_Fight
    battle.startTick = GameWorld.GetGameWorld().GetTick()
    tick = GameWorld.GetGameWorld().GetTick()
    battle.ChangeBattleState(ChConfig.Def_MirrorBatState_Fight, tick)
    return
def ProcessPlayerMirrorAI(curPlayer, tick):
@@ -576,31 +659,39 @@
    battle = GetMirrorBattle(curPlayer)
    if not battle:
        return
    playerID = curPlayer.GetPlayerID()
    realPlayerID = curPlayer.GetRealPlayerID()
    if realPlayerID:
        # 用第一个对手镜像玩家来定时判断战斗阶段,因为玩家自身可能会掉线,所以不用自己,而用镜像玩家
        if realPlayerID == battle.GetTagPlayerID():
            if battle.stateTick and battle.CaclStateTick(tick) <= 0:
                if battle.batState == ChConfig.Def_MirrorBatState_Prepare:
                    OnMirrorBattleStart(battle.battleID)
                elif battle.batState == ChConfig.Def_MirrorBatState_Fight:
                    OnMirrorAttackOver(battle.battleID)
                elif battle.batState == ChConfig.Def_MirrorBatState_Over:
                    ClearMirrorBattleByID(battle.battleID)
    else:
        # 常规战斗下,真实玩家不处理,由玩家自行控制
        if not battle.isQuick:
            return
    if battle.batState != ChConfig.Def_MirrorBatState_Fight:
        #GameWorld.DebugLog("镜像玩家仅自由战斗状态下需要处理! battleID=%s,batState=%s" % (battle.battleID, battle.batState), playerID)
        return
    
    if not battle.isQuick:
        realPlayerID = curPlayer.GetRealPlayerID()
        if not realPlayerID:
            # 常规战斗下,真实玩家不处理,由玩家自行控制
            # 真实玩家附加判断是否PK超时
            if battle.startTick and battle.CaclFightTick(tick) <= 0:
                OnMirrorAttackOver(battle.battleID)
            return
    if GameObj.GetHP(curPlayer) <= 0:
        #GameWorld.DebugLog("镜像玩家已被击杀", playerID)
        return
    
    # 攻击间隔
    if tick - curPlayer.GetPlayerAttackTick() < curPlayer.GetAtkInterval():
        GameWorld.DebugLog("攻击间隔: %s < %s" % (tick - curPlayer.GetPlayerAttackTick(), curPlayer.GetAtkInterval()), playerID)
        #GameWorld.DebugLog("攻击间隔: %s < %s" % (tick - curPlayer.GetPlayerAttackTick(), curPlayer.GetAtkInterval()), playerID)
        return
    
    autoUseSkillList = battle.GetPlayerAutoUseSkillList(curPlayer)
    GameWorld.DebugLog("镜像AI攻击: autoUseSkillList=%s" % (autoUseSkillList), playerID)
    #GameWorld.DebugLog("镜像AI攻击: autoUseSkillList=%s" % (autoUseSkillList), playerID)
    
    if curPlayer.GetPlayerVehicle() == IPY_GameWorld.pvHorse:
        PlayerHorse.PlayerRideHorseDown(curPlayer)
@@ -670,10 +761,10 @@
        curPlayer.SetUseSkill(curSkill.GetSkillData())
        useSkillData = curPlayer.GetUseSkill()
        if not PlayerState.__DoClientUseSkillEx(curPlayer, useSkillData, tick):
            GameWorld.DebugLog("        技能攻击失败: playerID=%s,tagID=%s,skillID=%s" % (playerID, tagObjID, skillID))
            #GameWorld.DebugLog("        技能攻击失败: playerID=%s,tagID=%s,skillID=%s" % (playerID, tagObjID, skillID))
            continue
        useSkillResult = True
        GameWorld.DebugLog("        技能攻击成功: playerID=%s,tagID=%s,skillID=%s" % (playerID, tagObjID, skillID))
        #GameWorld.DebugLog("        技能攻击成功: playerID=%s,tagID=%s,skillID=%s" % (playerID, tagObjID, skillID))
        
        if useSkillData and useSkillData.GetSkillID() != ChConfig.Def_SkillID_Somersault:
            # 跟随玩家同频率攻击
@@ -735,7 +826,7 @@
    battle = GetMirrorBattleByID(battleID)
    if not battle:
        return
    if battle.batState > ChConfig.Def_MirrorBatState_Fight:
    if battle.batState != ChConfig.Def_MirrorBatState_Fight:
        return
    if battle.isQuick:
        #不重复触发处理
@@ -747,22 +838,16 @@
            if mapID in quickLimitMapIDList:
                GameWorld.DebugLog("战斗中不允许点击快速战斗! mapID=%s" % mapID, battle.playerID)
                return
        if isLogout:
            logoutQuickLimitMapIDList = IpyGameDataPY.GetFuncEvalCfg("MirrorAttack", 3)
            if mapID in logoutQuickLimitMapIDList:
                GameWorld.DebugLog("掉线不允许快速战斗! mapID=%s" % mapID, battle.playerID)
                return
    tick = GameWorld.GetGameWorld().GetTick()
    battle.batState = ChConfig.Def_MirrorBatState_Fight
    battle.isQuick = True
    battle.isLogout = isLogout
    battle.CaclFightTick(tick)
    battle.CaclStateTick(tick)
    
    playerMgr = GameWorld.GetMapCopyPlayerManager()
    perLoopTick = 100 # 每次循环视为已过毫秒
    maxLoopCount = battle.fightTickRemain / perLoopTick # 循环次数上限
    GameWorld.DebugLog("DoMirrorBattleQuick isLogout=%s,maxLoopCount=%s,tick=%s,fightTickRemain=%s"
                       % (isLogout, maxLoopCount, tick, battle.fightTickRemain), battleID)
    maxLoopCount = battle.stateTickRemain / perLoopTick # 循环次数上限
    GameWorld.DebugLog("镜像PK快速结算: isLogout=%s,maxLoopCount=%s,tick=%s,stateTickRemain=%s"
                       % (isLogout, maxLoopCount, tick, battle.stateTickRemain), battleID)
    
    # 屏蔽发包
    for batPlayerID in battle.realIDDict.keys():
@@ -776,8 +861,7 @@
            # 可能还没循环完毕就结束了
            break
        tick += perLoopTick # 修改每次循环的tick
        battle.CaclFightTick(tick)
        GameWorld.DebugLog("    loopCount=%s,tick=%s,fightTickRemain=%s" % (loopCount, tick, battle.fightTickRemain), battleID)
        #GameWorld.DebugLog("    loopCount=%s,tick=%s,stateTickRemain=%s" % (loopCount, tick, battle.stateTickRemain), battleID)
        for batPlayerID in battle.playerFactionDict.keys():
            if batPlayerID in battle.deadPlayerIDList:
                continue
@@ -846,10 +930,9 @@
    if battle.batState >= ChConfig.Def_MirrorBatState_Over:
        # 已经结算过
        return
    battle.batState = ChConfig.Def_MirrorBatState_Over
    tick = GameWorld.GetGameWorld().GetTick()
    if not battle.fightTickRemain:
        battle.CaclFightTick(tick)
    if not battle.stateTickRemain:
        battle.CaclStateTick(tick)
    # 暂定没击杀算输,发起方为1
    if not battle.winFaction:
        battle.winFaction = 2
@@ -882,9 +965,72 @@
                curPlayer.SetDead(curPlayer.GetDictByKey(ChConfig.Def_NPCDead_KillerID),
                                  curPlayer.GetDictByKey(ChConfig.Def_NPCDead_KillerType))
                
    #统计明细
    for playerID, faction in battle.playerFactionDict.items():
        player = playerMgr.FindPlayerByID(playerID)
        if not player:
            continue
        realPlayerID = player.GetRealPlayerID()
        hp = GameObj.GetHP(player)
        hpMax = GameObj.GetMaxHP(player)
        GameWorld.DebugLog("剩余血量: %s/%s, 护盾:%s/%s,playerID=%s,realPlayerID=%s,faction=%s"
                           % (hp, hpMax, PlayerControl.GetProDef(player), PlayerControl.GetMaxProDef(player), playerID, realPlayerID, faction), battleID)
        if faction == 1:
            battle.curHP += hp
            battle.curHPMax += hpMax
        else:
            battle.tagHP += hp
            battle.tagHPMax += hpMax
    FBLogic.OnMirrorBattleOver(battleID, mapID)
    
    if battle.isSysbg:
        ClearMirrorBattleByID(battleID)
    # 改变状态需放在最后
    battle.ChangeBattleState(ChConfig.Def_MirrorBatState_Over, tick)
    return
def DoPlayerLeaveFB(curPlayer, tick):
    ##玩家主动离开副本
    if curPlayer.GetRealPlayerID():
        return
    battle = GetMirrorBattle(curPlayer)
    if not battle:
        return
    GameWorld.DebugLog("玩家主动退出镜像战斗强制快速结算战斗!", curPlayer.GetPlayerID())
    DoQuickOverByExit(curPlayer)
    return
def DoExitFB(curPlayer, tick):
    ##玩家退出副本
    if curPlayer.GetRealPlayerID():
        return
    battle = GetMirrorBattle(curPlayer)
    if not battle:
        return
    # 退出时还有战场数据一般是掉线的,因为主动退出默认强制结算
    mapID = battle.mapID
    logoutQuickLimitMapIDList = IpyGameDataPY.GetFuncEvalCfg("MirrorAttack", 3)
    if mapID in logoutQuickLimitMapIDList:
        GameWorld.DebugLog("掉线不允许快速战斗! mapID=%s" % mapID, battle.playerID)
        return
    GameWorld.DebugLog("玩家掉线退出镜像战斗快速结算战斗!", curPlayer.GetPlayerID())
    DoQuickOverByExit(curPlayer)
    return
def DoQuickOverByExit(curPlayer):
    ## 执行退出快速结算
    battle = GetMirrorBattle(curPlayer)
    if not battle:
        return
    battleID = battle.battleID
    if battle.batState < ChConfig.Def_MirrorBatState_Fight:
        OnMirrorBattleStart(battleID) # 还未开始则强制开始
    # 如果还在战斗中,直接快速执行战斗结果
    if battle.batState == ChConfig.Def_MirrorBatState_Fight:
        DoMirrorBattleQuick(battle.battleID, isLogout=True)
    ClearMirrorBattleByID(battleID)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -1888,6 +1888,9 @@
#前端自定义场景地图
ClientCustomSceneList = [Def_FBMapID_PersonalBoss, Def_FBMapID_ArenaBattle, Def_FBMapID_MirrorBattle]
#镜像PK的地图ID列表
MirrorBattleMapIDList = [Def_FBMapID_MirrorBattle, Def_FBMapID_ArenaBattle, Def_FBMapID_CrossRealmPK, Def_FBMapID_CrossChampionship]
#注册上传跨服服务器数据后直接进入跨服服务器的地图
RegisterEnter_CrossServerMapIDList = [Def_FBMapID_CrossPenglai, Def_FBMapID_CrossDemonLand, Def_FBMapID_CrossDemonKing, 
                                      Def_FBMapID_CrossGrasslandLing, Def_FBMapID_CrossGrasslandXian, Def_FBMapID_CrossBattlefield,
@@ -1927,15 +1930,16 @@
# 副本中玩家下线就被踢出
Def_DisconnectExit_FBID = []
#会有阵营的地图
Def_MapID_NeedCamp = [Def_FBMapID_FamilyWar, Def_FBMapID_GatherSoul, Def_FBMapID_CrossRealmPK, Def_FBMapID_CrossBattlefield]
Def_MapID_NeedCamp = [Def_FBMapID_FamilyWar, Def_FBMapID_GatherSoul, Def_FBMapID_CrossBattlefield] + MirrorBattleMapIDList
# 进入副本需要根据请求lineID动态分配虚拟分线属性的地图
Def_MapID_LineIDToPropertyID = [Def_FBMapID_ElderBattlefield]
                      
# 进入副本需要发送到GameServer的地图
Def_MapID_SendToGameServer = [Def_FBMapID_HorsePetBoss, Def_FBMapID_FamilyInvade, Def_FBMapID_SealDemon, Def_FBMapID_DemonKing,
                              Def_FBMapID_FamilyWar, Def_FBMapID_ZhuXianBoss, Def_FBMapID_AllFamilyBoss] + Def_MapID_LineIDToPropertyID + [Def_FBMapID_CrossChampionship]
                              Def_FBMapID_FamilyWar, Def_FBMapID_ZhuXianBoss, Def_FBMapID_AllFamilyBoss] + \
                              Def_MapID_LineIDToPropertyID + [Def_FBMapID_CrossChampionship] + MirrorBattleMapIDList
# 刷新标识点在无玩家的情况下也需要刷新的地图
Def_NoPlayerNeedProcessRefreshPointMap = [Def_FBMapID_HorsePetBoss, Def_FBMapID_SealDemon, Def_FBMapID_GodArea, Def_FBMapID_BossHome, Def_FBMapID_GatherSoul, Def_FBMapID_ZhuXianBoss, Def_FBMapID_AllFamilyBoss]
@@ -1952,9 +1956,6 @@
# 从副本退出时,可以返回进入前的副本ID
Def_CanBackFBMap = []
# 当日换战盟不可进入的地图 (本项目为24小时)
Def_ChangeFamilyCanNotEnterMap = []
#答题触发地图
Def_Subject_Map = [
@@ -2003,9 +2004,9 @@
                'Guard':[Def_FBMapID_Guard], #守护副本
                'SealDemon':[Def_FBMapID_SealDemon, Def_FBMapID_SealDemonEx], #封魔坛
                'XMZZ':[Def_FBMapID_XMZZ], #仙魔之争
                'ArenaBattle':[Def_FBMapID_ArenaBattle],#竞技场战斗
                'CrossRealmPK':[Def_FBMapID_CrossRealmPK], #跨服竞技场
                'CrossChampionship':[Def_FBMapID_CrossChampionship], #跨服排位
                #'ArenaBattle':[Def_FBMapID_ArenaBattle],#竞技场战斗
                #'CrossRealmPK':[Def_FBMapID_CrossRealmPK], #跨服竞技场
                #'CrossChampionship':[Def_FBMapID_CrossChampionship], #跨服排位
                'CrossDemonKing':[Def_FBMapID_DemonKing, Def_FBMapID_CrossDemonKing], #妖王
                'CrossGrassland':[Def_FBMapID_CrossGrasslandLing, Def_FBMapID_CrossGrasslandXian], #草园
                'GatherSoul':[Def_FBMapID_GatherSoul],#聚魂副本
@@ -2019,7 +2020,7 @@
                'CrossBattlefield':[Def_FBMapID_CrossBattlefield], #跨服战场
                'CrossFamilyFlagwar':[Def_FBMapID_CrossFamilyFlagwar], #跨服仙盟夺旗战/逐鹿万界
                'MineArea':[Def_TFMapID_MineArea], #福地
                'MirrorBattle':[Def_FBMapID_MirrorBattle], #镜像切磋
                'MirrorBattle':MirrorBattleMapIDList, #镜像切磋
                }
#特殊副本ID, 由系统分配, 进入时候不验证IsMapCopyFull
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py
@@ -18,7 +18,6 @@
import GameWorld
import MirrorAttack
import PlayerViewCacheTube
import PlayerFB
import ChConfig
## GM命令执行入口
@@ -29,63 +28,64 @@
def OnExec(curPlayer, paramList):
    if not paramList:
        GameWorld.DebugAnswer(curPlayer, "-------------------%s" % GameWorld.GetCurrentDataTimeStr())
        GameWorld.DebugAnswer(curPlayer, "创建战斗: PlayerMirror c 是否后台 [目标ID ID2 ...]")
        GameWorld.DebugAnswer(curPlayer, "开始战斗: PlayerMirror s")
        GameWorld.DebugAnswer(curPlayer, "后台战斗: PlayerMirror s [玩家ID1 ID2 ...]")
        GameWorld.DebugAnswer(curPlayer, "切图战斗: PlayerMirror m [功能地图ID 线路 目标ID]")
        GameWorld.DebugAnswer(curPlayer, "跳过战斗: PlayerMirror q")
        GameWorld.DebugAnswer(curPlayer, "退出战斗: PlayerMirror e")
        GameWorld.DebugAnswer(curPlayer, "更新镜像: PlayerMirror 5")
        GameWorld.DebugAnswer(curPlayer, "加假数据: PlayerMirror a 个数 [起始ID 战力 区服ID 模版key]")
        GameWorld.DebugAnswer(curPlayer, "删假数据: PlayerMirror d")
        GameWorld.DebugAnswer(curPlayer, "输出数据: PlayerMirror p [起始索引 个数 ]")
        GameWorld.DebugAnswer(curPlayer, "是否后台:0-玩家自身参与战斗")
        GameWorld.DebugAnswer(curPlayer, "是否后台:1-玩家无感知,系统直接出结果")
        GameWorld.DebugAnswer(curPlayer, "目标ID:无-自己;>0-其他玩家ID支持跨服玩家ID")
        GameWorld.DebugAnswer(curPlayer, "目标ID多个时为多对多战斗")
        GameWorld.DebugAnswer(curPlayer, "多对多阵营分配均分AABBB即玩家和AA对BBB")
        GameWorld.DebugAnswer(curPlayer, "多对多阵营分配ID为前后前后即AABB")
        GameWorld.DebugAnswer(curPlayer, "后台战斗时ID阵营分配为[AABB]")
        GameWorld.DebugAnswer(curPlayer, "切图战斗时ID阵营分配为[自己ABB]")
        GameWorld.DebugAnswer(curPlayer, "玩家ID不填时默认自己跟自己打")
        return
    
    mapID = ChConfig.Def_FBMapID_MirrorBattle
    lineID = 0
    tick = GameWorld.GetGameWorld().GetTick()
    playerID = curPlayer.GetPlayerID()
    value1 = paramList[0]
    if value1 == "c":
        isSysbg = paramList[1] if len(paramList) > 1 else 0
        if not isSysbg:
            if not PlayerFB.DoEnterCustomScene(curPlayer, mapID, lineID, tick):
                GameWorld.DebugAnswer(curPlayer, "进入自定义PK创景失败:%s" % mapID)
                return
        mirrorIDList = paramList[2:]
    if value1 == "s":
        mapID = ChConfig.Def_FBMapID_MirrorBattle
        lineID = 0
        mirrorIDList = paramList[1:]
        if not mirrorIDList:
            mirrorIDList.append(playerID)
            mirrorIDList = [playerID, playerID]
        elif len(mirrorIDList) == 1:
            mirrorIDList = [playerID] + mirrorIDList
            
        factionIDListA, factionIDListB = [playerID], []
        reqPlayer = curPlayer if mirrorIDList[0] == playerID else None # 第一个ID是玩家ID时代表属于玩家发起的后台战斗
        requestID = playerID if curPlayer else GameWorld.GetGameWorld().GetTick()
        factionIDListA, factionIDListB = [], []
        while mirrorIDList:
            # 后面为对手
            factionIDListB.append(mirrorIDList.pop(-1))
            # 前面为队友
            factionIDListA.append(mirrorIDList.pop(0))
            if mirrorIDList:
                factionIDListA.append(mirrorIDList.pop(0))
        posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
        battlePlayerList = []
        for i, batPlayerID in enumerate(factionIDListA):
            battlePlayerList.append({"playerID":batPlayerID, "faction":1, "posX":posX, "posY":posY + i * 5})
        for i, batPlayerID in enumerate(factionIDListB):
            battlePlayerList.append({"playerID":batPlayerID, "faction":2, "posX":posX + 5, "posY":posY + i * 5})
                factionIDListB.append(mirrorIDList.pop(-1))
        factionPlayerList = [factionIDListA, factionIDListB]
        if MirrorAttack.OnRequestCreateMirrorBattle(mapID, lineID, requestID, factionPlayerList, True, reqPlayer):
            GameWorld.DebugAnswer(curPlayer, "后台战斗: %s VS %s" % (factionIDListA, factionIDListB))
        else:
            GameWorld.DebugAnswer(curPlayer, "后台战斗失败: %s VS %s" % (factionIDListA, factionIDListB))
    elif value1 == "m":
        mapID = paramList[1] if len(paramList) > 1 else ChConfig.Def_FBMapID_MirrorBattle
        lineID = paramList[2] if len(paramList) > 2 else 0
        mirrorIDList = [playerID] + paramList[3:]
        if len(mirrorIDList) < 2:
            mirrorIDList.append(playerID) # 默认和自己打
            
        requestID = playerID
        if MirrorAttack.OnRequestCreateMirrorBattle(mapID, lineID, requestID, battlePlayerList, isSysbg, curPlayer):
            GameWorld.DebugAnswer(curPlayer, "请求创建镜像: %s VS %s" % (factionIDListA, factionIDListB))
        else:
            GameWorld.DebugAnswer(curPlayer, "请求创建镜像失败: %s VS %s" % (factionIDListA, factionIDListB))
    elif value1 == "s":
        battle = MirrorAttack.GetMirrorBattle(curPlayer)
        if battle:
            MirrorAttack.OnMirrorBattleStart(battle.battleID)
        factionIDListA, factionIDListB = [], []
        while mirrorIDList:
            factionIDListA.append(mirrorIDList.pop(0))
            if mirrorIDList:
                factionIDListB.append(mirrorIDList.pop(-1))
        factionPlayerList = [factionIDListA, factionIDListB]
        reqOK = MirrorAttack.OnRequestCreateMirrorBattle(mapID, lineID, requestID, factionPlayerList, False, curPlayer, True)
        GameWorld.DebugAnswer(curPlayer, "切图战斗: %s VS %s, %s" % (factionIDListA, factionIDListB, reqOK))
    elif value1 == "q":
        battle = MirrorAttack.GetMirrorBattle(curPlayer)
        if battle:
@@ -93,9 +93,6 @@
                GameWorld.DebugAnswer(curPlayer, "无法执行快速战斗,详见地图日志!")
                return
            
    elif value1 == "e":
        PlayerFB.DoExitCustomScene(curPlayer)
    elif value1 == 5:
        tick = GameWorld.GetGameWorld().GetTick()
        PlayerViewCacheTube.UpdateGameServerPlayerCache(curPlayer, tick, forcePackData=True)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
@@ -2499,7 +2499,7 @@
    
    return callFunc(curPlayer, mapID, funcLineID, tagType, tagID, valueList, fightRet, awardItemList, ret)
def OnMirrorBattleRequest(curPlayer, mapID, funcLineID):
def OnMirrorBattleRequest(curPlayer, mapID, funcLineID, factionPlayerList):
    ## 镜像战斗请求
    do_FBLogic_ID = __GetFBLogic_MapID(mapID)
    
@@ -2509,7 +2509,7 @@
        # 默认允许
        return True
    
    return callFunc(curPlayer, mapID, funcLineID)
    return callFunc(curPlayer, mapID, funcLineID, factionPlayerList)
def OnMirrorBattleOver(battleID, mapID):
    ## 镜像战斗结束
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_ArenaBattle.py
@@ -14,30 +14,7 @@
#-------------------------------------------------------------------------------
#"""Version = 2020-12-07 19:30"""
#-------------------------------------------------------------------------------
import PlayerArena
## 是否能够通过活动查询进入
def OnEnterFBEvent(curPlayer, mapID, lineID, tick):
    return True
## 是否需要做进入副本通用检查条件逻辑,默认需要检查
def OnNeedCheckCanEnterFBComm(curPlayer, mapID, lineID):
    ## 进行中的不需要重复检查,防止断线重连被禁止进入
    return False
## 客户端进入自定义场景
def OnEnterCustomScene(curPlayer, mapID, lineID):
    return
## 判断可否召唤木桩怪
def OnCanSummonPriWoodPile(curPlayer, mapID, lineID, npcID, count):
    return True
## 自定义场景副本击杀NPC
def DoCustomScene_Player_KillNPC(curPlayer, curNPC, mapID, lineID):
    PlayerArena.OnKillBattleNPC(curPlayer, curNPC)
    return
#
# 改为使用镜像PK,逻辑统一放 GameLogic_MirrorBattle
#
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossChampionship.py
@@ -14,117 +14,6 @@
#-------------------------------------------------------------------------------
#"""Version = 2022-09-21 21:30"""
#-------------------------------------------------------------------------------
import GameWorld
import MirrorAttack
import IPY_GameWorld
import PlayerControl
import CrossRealmPlayer
import IpyGameDataPY
import ShareDefine
import FBCommon
import GameObj
###处理副本中杀死玩家逻辑
def DoFBOnKill_Player(atkobj, defender, tick):
    GameWorld.DebugLog("镜像切磋击杀玩家: defID=%s" % (defender.GetID()), atkobj.GetID())
    return True
def OnMirrorBattleRequest(curPlayer, mapID, funcLineID):
    ## 镜像战斗请求
    playerID = curPlayer.GetPlayerID()
    if GameWorld.IsCrossServer():
        return
    if not CrossRealmPlayer.IsCrossServerOpen():
        PlayerControl.NotifyCode(curPlayer, "CrossMatching18")
        return
    if curPlayer.GetPlayerAction() == IPY_GameWorld.paDie or GameObj.GetHP(curPlayer) == 0:
        GameWorld.DebugLog("已死亡,无法进行跨服排位赛!", playerID)
        return
    stateError = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_CrossChampionshipStateError)
    if stateError:
        GameWorld.ErrLog("跨服排位状态已经异常无法进入! stateError=%s" % stateError, playerID)
        return
    state = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_CrossChampionshipState)
    if state not in ShareDefine.CrossChampionshipEnterStateInfo:
        GameWorld.ErrLog("当前状态非跨服排位战斗状态无法进入: state=%s" % state, playerID)
        return
    groupMark = ShareDefine.CrossChampionshipEnterStateInfo[state]
    reqGroupMark = funcLineID % 100
    if reqGroupMark != groupMark:
        GameWorld.ErrLog("当前状态与跨服排位战斗分组不一致无法进入: funcLineID=%s,reqGroupMark=%s != %s" % (funcLineID, reqGroupMark, groupMark), playerID)
        return
    return True
def OnMirrorBattleOver(battleID):
    ## 镜像战斗结束
    battle = MirrorAttack.GetMirrorBattleByID(battleID)
    if not battle:
        return
    mapID = battle.mapID
    funcLineID = battle.funcLineID
    isWin = battle.isWin
    curPlayerID = battle.requestID # 副本所属玩家ID,该玩家不一定参与实际战斗
    tagPlayerID = battle.GetTagPlayerID()
    fightTickRemain = battle.fightTickRemain
    curHPPer = 0 # 自己剩余血量百分比 0~100,支持小数点
    tagHPPer = 0 # 对方剩余血量百分比 0~100,支持小数点
    curPlayer = None
    playerMgr = GameWorld.GetMapCopyPlayerManager()
    for playerID, faction in battle.playerFactionDict.items():
        player = playerMgr.FindPlayerByID(playerID)
        if not player:
            continue
        realPlayerID = player.GetRealPlayerID()
        hp = GameObj.GetHP(player)
        hpMax = GameObj.GetMaxHP(player)
        hpPer = hp / float(hpMax) * 100
        GameWorld.DebugLog("    剩余血量: %s/%s,hpPer=%s%%,playerID=%s,realPlayerID=%s,faction=%s"
                           % (hp, hpMax, hpPer, playerID, realPlayerID, faction), battleID)
        if not realPlayerID and curPlayerID == playerID:
            curPlayer = player
            curHPPer = hpPer
        if tagPlayerID == realPlayerID:
            tagHPPer = hpPer
    if not curPlayer:
        return
    baseScoreList = IpyGameDataPY.GetFuncEvalCfg("CrossChamMirrorPK", 2)
    baseScore = 0
    if baseScoreList and len(baseScoreList) == 2:
        baseScore = baseScoreList[0] if battle.isWin else baseScoreList[1]
    hpScore = int(eval(IpyGameDataPY.GetFuncCompileCfg("CrossChamMirrorPK", 3)))
    GameWorld.DebugLog("    hpScore=%s,curHPPer=%s,tagHPPer=%s" % (hpScore, curHPPer, tagHPPer), battleID)
    remainTimePer = fightTickRemain / float(battle.fightTickMax) * 100 # 剩余战斗时间百分比
    timeScore = int(eval(IpyGameDataPY.GetFuncCompileCfg("CrossChamMirrorPK", 4)))
    addScore = baseScore + hpScore + timeScore
    GameWorld.DebugLog("    timeScore=%s,remainTimePer=%s%%,fightTickRemain=%s,fightTickMax=%s" % (timeScore, remainTimePer, fightTickRemain, battle.fightTickMax), battleID)
    pkCountMax = IpyGameDataPY.GetFuncCfg("CrossChamMirrorPK", 1)
    playerID = curPlayer.GetPlayerID()
    dataMsg = {
               "playerID":playerID,
               "tagPlayerID":tagPlayerID,
               "funcLineID":funcLineID,
               "isWin":isWin,
               "addScore":addScore,
               "baseScore":baseScore,
               "hpScore":hpScore,
               "timeScore":timeScore,
               "pkCountMax":pkCountMax,
               }
    GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_ChampionshipPKOver, dataMsg)
    overDict = {"isWin":isWin, "tagPlayerID":tagPlayerID, "addScore":addScore, "baseScore":baseScore, "hpScore":hpScore, "timeScore":timeScore}
    FBCommon.NotifyFBOver(curPlayer, mapID, funcLineID, isWin, overDict)
    return
#
# 改为使用镜像PK,逻辑统一放 GameLogic_MirrorBattle
#
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossRealmPK.py
@@ -14,65 +14,6 @@
#-------------------------------------------------------------------------------
#"""Version = 2018-12-21 18:00"""
#-------------------------------------------------------------------------------
import GameWorld
import MirrorAttack
import PlayerCrossRealmPK
import GameObj
###处理副本中杀死玩家逻辑
def DoFBOnKill_Player(atkobj, defender, tick):
    GameWorld.DebugLog("镜像切磋击杀玩家: defID=%s" % (defender.GetID()), atkobj.GetID())
    return True
def OnMirrorBattleRequest(curPlayer, mapID, funcLineID):
    ## 镜像战斗请求
    if not PlayerCrossRealmPK.CheckCanMatch(curPlayer):
        return
    if not PlayerCrossRealmPK.CheckHavePKCount(curPlayer):
        return
    return True
def OnMirrorBattleOver(battleID):
    ## 镜像战斗结束
    battle = MirrorAttack.GetMirrorBattleByID(battleID)
    if not battle:
        return
    isLogout = battle.isLogout
    mapID = battle.mapID
    funcLineID = battle.funcLineID
    winFaction = battle.winFaction
    curPlayerID = battle.requestID # 副本所属玩家ID,该玩家不一定参与实际战斗
    curIsWin = 0
    tagPlayerID = 0
    GameWorld.DebugLog("镜像战斗结算: mapID=%s,funcLineID=%s,winFaction=%s,isLogout=%s" % (mapID, funcLineID, winFaction, isLogout), battleID)
    playerMgr = GameWorld.GetMapCopyPlayerManager()
    for playerID, faction in battle.playerFactionDict.items():
        curPlayer = playerMgr.FindPlayerByID(playerID)
        if not curPlayer:
            continue
        realPlayerID = curPlayer.GetRealPlayerID()
        isWin = (faction == winFaction)
        GameWorld.DebugLog("剩余血量: %s/%s,playerID=%s,realPlayerID=%s,faction=%s,isWin=%s"
                           % (GameObj.GetHP(curPlayer), GameObj.GetMaxHP(curPlayer), playerID, realPlayerID, faction, isWin), battleID)
        if isWin and faction == 1:
            curIsWin = 1
        if faction != 1 and not tagPlayerID:
            tagPlayerID = realPlayerID
    if not curPlayerID:
        return
    curPlayer = playerMgr.FindPlayerByID(curPlayerID)
    if not curPlayer:
        return
    PlayerCrossRealmPK.SendPKOver(curPlayer, tagPlayerID, curIsWin)
    return
#
# 改为使用镜像PK,逻辑统一放 GameLogic_MirrorBattle
#
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py
@@ -15,22 +15,101 @@
#"""Version = 2024-10-17 15:00"""
#-------------------------------------------------------------------------------
import GameWorld
import ChConfig
import ShareDefine
import MirrorAttack
import PlayerControl
import IpyGameDataPY
import PlayerCrossRealmPK
import CrossRealmPlayer
import PlayerArena
import GameWorld
import FBCommon
import GameObj
## 是否能够通过活动查询进入
def OnEnterFBEvent(curPlayer, mapID, lineID, tick):
    return True
def OnGetFBEnterPos(curPlayer, mapID, lineId, ipyEnterPosInfo, tick):
    return MirrorAttack.OnGetMirrorBattlePos(mapID, lineId)[0]
## 客户端进入自定义场景
def OnEnterCustomScene(curPlayer, mapID, lineID):
def DoEnterFB(curPlayer, tick):
    playerID = curPlayer.GetPlayerID()
    GameWorld.Log("---------------- 镜像战斗DoEnterFB ----------------", playerID)
    MirrorAttack.OnMirrorBattleEnterMapInit(curPlayer, tick)
    return
def DoExitFB(curPlayer, tick):
    ##玩家退出副本
    MirrorAttack.DoExitFB(curPlayer, tick)
    return
def DoPlayerLeaveFB(curPlayer, tick):
    ##玩家主动离开副本
    MirrorAttack.DoPlayerLeaveFB(curPlayer, tick)
    return
def CheckPlayersRelation_IsFriend(curPlayer, curTagPlayer):
    return not CanAttackPlayer(curPlayer, curTagPlayer)
def CanAttackPlayer(curPlayer, curTagPlayer):
    battle = MirrorAttack.GetMirrorBattle(curPlayer)
    if not battle:
        return
    if battle.batState != ChConfig.Def_MirrorBatState_Fight:
        return
    if curTagPlayer:
        if curPlayer.GetFaction() == curTagPlayer.GetFaction():
            return
    return True
##处理副本中杀死玩家逻辑
def DoFBOnKill_Player(atkobj, defender, tick):
    GameWorld.DebugLog("镜像切磋击杀玩家: defID=%s" % (defender.GetID()), atkobj.GetID())
    return True
def OnMirrorBattleRequest(curPlayer, mapID, funcLineID, factionPlayerList):
    ## 镜像战斗请求
    if mapID == ChConfig.Def_FBMapID_CrossRealmPK:
        return __CheckRequest_CrossRealmPK(curPlayer, mapID, funcLineID)
    if mapID == ChConfig.Def_FBMapID_CrossChampionship:
        return __CheckRequest_CrossChampionship(curPlayer, mapID, funcLineID)
    return True
def __CheckRequest_CrossRealmPK(curPlayer, mapID, funcLineID):
    if not CrossRealmPlayer.IsCrossServerOpen():
        PlayerControl.NotifyCode(curPlayer, "CrossMatching18")
        return
    if not PlayerCrossRealmPK.CheckCanMatch(curPlayer):
        return
    if not PlayerCrossRealmPK.CheckHavePKCount(curPlayer):
        return
    return True
def __CheckRequest_CrossChampionship(curPlayer, mapID, funcLineID):
    playerID = curPlayer.GetPlayerID()
    if not CrossRealmPlayer.IsCrossServerOpen():
        PlayerControl.NotifyCode(curPlayer, "CrossMatching18")
        return
    stateError = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_CrossChampionshipStateError)
    if stateError:
        GameWorld.ErrLog("跨服排位状态已经异常无法进入! stateError=%s" % stateError, playerID)
        return
    state = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_CrossChampionshipState)
    if state not in ShareDefine.CrossChampionshipEnterStateInfo:
        GameWorld.ErrLog("当前状态非跨服排位战斗状态无法进入: state=%s" % state, playerID)
        return
    groupMark = ShareDefine.CrossChampionshipEnterStateInfo[state]
    reqGroupMark = funcLineID % 100
    if reqGroupMark != groupMark:
        GameWorld.ErrLog("当前状态与跨服排位战斗分组不一致无法进入: funcLineID=%s,reqGroupMark=%s != %s" % (funcLineID, reqGroupMark, groupMark), playerID)
        return
    return True
def OnMirrorBattleOver(battleID):
@@ -39,35 +118,73 @@
    battle = MirrorAttack.GetMirrorBattleByID(battleID)
    if not battle:
        return
    isLogout = battle.isLogout
    mapID = battle.mapID
    funcLineID = battle.funcLineID
    winFaction = battle.winFaction
    curPlayerID = battle.requestID # 副本所属玩家ID,该玩家不一定参与实际战斗
    curIsWin = 0
    GameWorld.DebugLog("镜像战斗结算: mapID=%s,funcLineID=%s,winFaction=%s,isLogout=%s" % (mapID, funcLineID, winFaction, isLogout), battleID)
    playerMgr = GameWorld.GetMapCopyPlayerManager()
    for playerID, faction in battle.playerFactionDict.items():
        curPlayer = playerMgr.FindPlayerByID(playerID)
        if not curPlayer:
            continue
        realPlayerID = curPlayer.GetRealPlayerID()
        isWin = (faction == winFaction)
        GameWorld.DebugLog("剩余血量: %s/%s,playerID=%s,realPlayerID=%s,faction=%s,isWin=%s"
                           % (GameObj.GetHP(curPlayer), GameObj.GetMaxHP(curPlayer), playerID, realPlayerID, faction, isWin), battleID)
        if isWin and faction == 1:
            curIsWin = 1
    if not curPlayerID:
        return
    curPlayer = playerMgr.FindPlayerByID(curPlayerID)
    isWin = battle.isWin
    curPlayerID = battle.playerID # 副本所属玩家ID,该玩家不一定参与实际战斗
    tagPlayerID = battle.GetTagPlayerID()
    curPlayer = GameWorld.GetMapCopyPlayerManager().FindPlayerByID(curPlayerID)
    if not curPlayer:
        return
    # 结算奖励,通知结果
    if mapID == ChConfig.Def_FBMapID_ArenaBattle:
        arenaPlayerID = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_ArenaBattleTagID)
        if tagPlayerID == arenaPlayerID:
            PlayerArena.SendGameServer_ArenaBattleOver(curPlayer, isWin)
        else:
            GameWorld.DebugLog("非竞技场对手不结算! tagPlayerID=%s,arenaPlayerID=%s" % (tagPlayerID, arenaPlayerID), curPlayerID)
        return
    if mapID == ChConfig.Def_FBMapID_CrossRealmPK:
        PlayerCrossRealmPK.SendPKOver(curPlayer, tagPlayerID, isWin)
        return
    if mapID == ChConfig.Def_FBMapID_CrossChampionship:
        __MirrorBattleOver_CrossChampionship(curPlayer, battle)
        return
    giveItemList = []
    overDict = {"isWin":curIsWin, FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(giveItemList)}
    FBCommon.NotifyFBOver(curPlayer, mapID, funcLineID, isWin, overDict)
    overDict = {"isWin":isWin, FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(giveItemList)}
    FBCommon.NotifyFBOver(curPlayer, mapID, funcLineID, isWin, overDict)
    return
def __MirrorBattleOver_CrossChampionship(curPlayer, battle):
    playerID = curPlayer.GetPlayerID()
    mapID = battle.mapID
    funcLineID = battle.funcLineID
    isWin = battle.isWin
    tagPlayerID = battle.GetTagPlayerID()
    curHPPer, tagHPPer = battle.CalcHPPer()
    remainTimePer = battle.CalcRemainTimePer()
    baseScoreList = IpyGameDataPY.GetFuncEvalCfg("CrossChamMirrorPK", 2)
    baseScore = 0
    if baseScoreList and len(baseScoreList) == 2:
        baseScore = baseScoreList[0] if isWin else baseScoreList[1]
    hpScore = int(eval(IpyGameDataPY.GetFuncCompileCfg("CrossChamMirrorPK", 3)))
    GameWorld.DebugLog("    hpScore=%s,curHPPer=%s,tagHPPer=%s" % (hpScore, curHPPer, tagHPPer), playerID)
    timeScore = int(eval(IpyGameDataPY.GetFuncCompileCfg("CrossChamMirrorPK", 4)))
    GameWorld.DebugLog("    timeScore=%s,remainTimePer=%s%%" % (timeScore, remainTimePer), playerID)
    addScore = baseScore + hpScore + timeScore
    GameWorld.DebugLog("    addScore=%s,baseScore=%s" % (addScore, baseScore), playerID)
    pkCountMax = IpyGameDataPY.GetFuncCfg("CrossChamMirrorPK", 1)
    playerID = curPlayer.GetPlayerID()
    dataMsg = {
               "playerID":playerID,
               "tagPlayerID":tagPlayerID,
               "funcLineID":funcLineID,
               "isWin":isWin,
               "addScore":addScore,
               "baseScore":baseScore,
               "hpScore":hpScore,
               "timeScore":timeScore,
               "pkCountMax":pkCountMax,
               }
    GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_ChampionshipPKOver, dataMsg)
    overDict = {"isWin":isWin, "tagPlayerID":tagPlayerID, "addScore":addScore, "baseScore":baseScore, "hpScore":hpScore, "timeScore":timeScore}
    FBCommon.NotifyFBOver(curPlayer, mapID, funcLineID, isWin, overDict)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerArena.py
@@ -199,6 +199,11 @@
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_ArenaBattleTagID, tagPlayerID)
        return
    
    if tagPlayerID >= 10000:
        if result:
            GameWorld.DebugLog("真人由后端镜像PK决定胜负! tagPlayerID=%s" % tagPlayerID, playerID)
            return
    isWin = 1 if result == 1 else 0
#    # 木桩被击杀,后端判断,其他前端同步
#    if isWin:
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -1358,8 +1358,6 @@
    
    PassiveBuffEffMng.OnPlayerLeaveMap(curPlayer)
    
    MirrorAttack.OnPlayerLeaveMap(curPlayer)
    #离开地图清空恶意攻击自己玩家信息
    if curPlayer.GetPlayerID() in PyGameData.g_maliciousAttackDict:
        PyGameData.g_maliciousAttackDict.pop(curPlayer.GetPlayerID())
@@ -1375,6 +1373,7 @@
    PyGameData.g_playerFuncAttrDict.pop(playerID, None)
    PyGameData.g_playerEquipPartAttrDict.pop(playerID, None)
    PyGameData.g_equipChangeClassLVInfo.pop(playerID, None)
    PyGameData.g_playerReqEnterFBEx.pop(playerID, None)
    NPCCommon.ClearPriWoodPile(curPlayer)
    #移除地图缓存的境界难度玩家ID信息
    for playerIDList in PyGameData.g_realmDiffPlayerDict.values():
@@ -1860,39 +1859,20 @@
# @param posY 坐标Y
# @return 返回值无意义
# @remarks 玩家单独进入副本
def PlayerEnterFB(curPlayer, mapID, lineID, posX=0, posY=0):
def PlayerEnterFB(curPlayer, mapID, lineID, posX=0, posY=0, reqInfoEx=None):
    mapID = FBCommon.GetRecordMapID(mapID)
    GameWorld.Log("玩家请求进入副本! mapID=%s,lineID=%s,posX=%s,posY=%s"
                  % (mapID, lineID, posX, posY), curPlayer.GetPlayerID())
    playerID = curPlayer.GetPlayerID()
    PyGameData.g_playerReqEnterFBEx[playerID] = reqInfoEx
    GameWorld.Log("玩家请求进入副本! mapID=%s,lineID=%s,posX=%s,posY=%s,reqInfoEx=%s" % (mapID, lineID, posX, posY, reqInfoEx), playerID)
    
    # 当日换战盟不可进入的地图, 改为按小时算
    #if mapID in ChConfig.Def_ChangeFamilyCanNotEnterMap:
    #    if PlayerFamily.GetPlayerChangeFamilyPastHour(curPlayer) < 24:
    #        NotifyCode(curPlayer, 'jiazu_xyj_671654')
    #        return
#    #跨服活动人数分流处理
#    if GameWorld.IsCrossServer():
#        reqMapID = mapID
#        mapID = __GetMergeFBPlayerMapID(curPlayer, reqMapID)
#        if not mapID:
#            GameWorld.ErrLog("找不到可分配进入的跨服活动地图ID! reqMapID=%s" % reqMapID)
#            return
    #过滤封包地图ID
    if not GameWorld.GetMap().IsMapIDExist(mapID):
        GameWorld.ErrLog('###非法地图数据,mapID: %s' % (mapID), curPlayer.GetID())
        return
#    if not GameWorld.GetMap().CanMove(curPlayer.GetPosX(), curPlayer.GetPosY()):
#        # 坐标不可移动则不能传送,不然会导致退出副本无法退回来源地
#        GameWorld.ErrLog("原障碍点无法切换地图 %s" % ([curPlayer.GetPosX(), curPlayer.GetPosY()]))
#        return
    #进入副本通用检查
    fbIpyData = FBCommon.GetFBIpyData(mapID)
    fbLineIpyData = FBCommon.GetFBLineIpyData(mapID, lineID)
    sceneMapID = mapID if not fbLineIpyData else fbLineIpyData.GetMapID()
    #过滤封包地图ID
    if not GameWorld.GetMap().IsMapIDExist(sceneMapID):
        GameWorld.ErrLog('###非法地图数据,sceneMapID: %s' % (sceneMapID), curPlayer.GetID())
        return
    tick = GameWorld.GetGameWorld().GetTick()
    if CheckMoveToFB(curPlayer, mapID, lineID, fbIpyData, fbLineIpyData, tick) != ShareDefine.EntFBAskRet_OK:
        return
@@ -1953,10 +1933,13 @@
        elif mapID in ChConfig.Def_MapID_LineIDToPropertyID:
            enterCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_EnterFbCntDay % mapID)
            extendParamList = [enterCnt]
        elif mapID in ChConfig.MirrorBattleMapIDList:
            reqInfoEx["sceneMapID"] = sceneMapID
            extendParamList = [reqInfoEx]
        SendToGameServerEnterFB(curPlayer, mapID, lineID, tick, extendParamList)
        return
    
    PlayerResetWorldPosFB(curPlayer, mapID, posX, posY, False, fbID, funcLineID=lineID)
    PlayerResetWorldPosFB(curPlayer, sceneMapID, posX, posY, False, fbID, funcLineID=lineID)
    return
##发送到GameServer请求进入副本
@@ -1977,7 +1960,7 @@
    #GameWorld.DebugLog("send GameServer_QueryPlayerByID")
    # 请求GameServer目标副本GameWorld索引
    sendMsg = [mapID, lineID]
    sendMsg.extend(extendParamList)
    sendMsg += extendParamList
    sendMsg = "%s" % sendMsg
    curPlayer.GameServer_QueryPlayerByID(ChConfig.queryType_EnterFB, 0, 'EnterFB', sendMsg, len(sendMsg))
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
@@ -38,7 +38,6 @@
import ShareDefine
import GameFuncComm
import FBHelpBattle
import MirrorAttack
import SkillShell
import PyGameData
import PetControl
@@ -513,7 +512,6 @@
    curPlayer.SetDict(ChConfig.Def_PlayerKey_ClientCustomSceneStepTick, tick)
    PlayerControl.SetCustomMap(curPlayer, mapID, lineID)
    NPCCommon.ClearPriWoodPile(curPlayer)
    MirrorAttack.ClearMirrorBattleByPlayer(curPlayer)
    GameWorld.Log("玩家开始自定义场景!mapID=%s,lineID=%s" % (mapID, lineID), playerID)
    if mapID:
        PetControl.DoLogic_PetLoadMapOK(curPlayer)
@@ -549,7 +547,6 @@
    if mapID and FBCommon.GetCustomMapStep(curPlayer, mapID, lineID) != ChConfig.CustomMapStep_Over:
        FBCommon.SetCustomMapStep(curPlayer, mapID, lineID, ChConfig.CustomMapStep_Over)
    NPCCommon.ClearPriWoodPile(curPlayer)
    MirrorAttack.ClearMirrorBattleByPlayer(curPlayer)
    
    #默认回满血
    if GameObj.GetHP(curPlayer) > 0 and curPlayer.GetPlayerAction() != IPY_GameWorld.paDie and GameObj.GetHP(curPlayer) < GameObj.GetMaxHP(curPlayer):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_EnterFB.py
@@ -271,6 +271,8 @@
        return
    
    # 传送逻辑
    PlayerControl.PlayerResetWorldPosFB(curPlayer, mapID, retPos[0], retPos[1], False, backFBID, funcLineID=funcLineID)
    fbLineIpyData = FBCommon.GetFBLineIpyData(mapID, funcLineID)
    sceneMapID = mapID if not fbLineIpyData else fbLineIpyData.GetMapID()
    PlayerControl.PlayerResetWorldPosFB(curPlayer, sceneMapID, retPos[0], retPos[1], False, backFBID, funcLineID=funcLineID)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py
@@ -18,6 +18,7 @@
import GameWorld
import MirrorAttack
import PlayerViewCacheTube
import PyGameData
#---------------------------------------------------------------------
#  @param query_Type 请求类型
@@ -33,6 +34,14 @@
    if msgType == "MirrorBattle":
        curPlayer = None
        playerID = msgInfo.get("playerID", 0)
        isChangeMap = msgInfo.get("isChangeMap", 0)
        # 是切图的战斗
        if isChangeMap and playerID:
            PyGameData.g_playerReqEnterFBEx[playerID] = [msgInfo, packDataDict]
            curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
            MirrorAttack.OnMirrorBattleEnterMapInit(curPlayer, tick)
            return
        if playerID:
            curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
            if not curPlayer or curPlayer.IsEmpty():
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
@@ -18,6 +18,7 @@
g_realmDiffPlayerDict = {} # 境界难度玩家信息 {realm:[playerID, ...], ...}
g_realmDiffNPCRefresh = {} # {(lineID, realm):{refreshID:tagNPCRefresh, ...}}
g_playerReqEnterFBEx = {} # 请求进入地图额外信息 {playerID:[...], ...}
g_commMapLinePlayerCountDict = {} # 常规地图分线人数 {mapID:{lineID:人数, ...}}
g_needRefreshMapServerState = True # 常规地图分线人数是否有变更需要通知
g_mapLastProcess_Minute = -1 # 地图上次处理的分钟