From cb132d533203f617129892c43c9be6a3dfecb27a Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期三, 15 一月 2025 12:00:54 +0800
Subject: [PATCH] 10263 【越南】【英文】【BT】后端支持NPC仿真实玩家战斗和快速战斗(改为真实地图战斗;竞技场、跨服PK、跨服排位赛,测试地图100均已支持;)

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py                                      |    3 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py                               |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_EnterFB.py                  |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py                                  |  422 +++++++++++++++-------
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py                                                           |   11 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py                                           |    1 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerArena.py                                   |    5 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py             |    9 
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py                                                     |   27 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py                             |   87 ++--
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossRealmPK.py      |   65 ---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossChampionship.py |  117 ------
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py                                 |   45 -
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_ArenaBattle.py       |   29 -
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py      |  183 ++++++++-
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                                             |   21 
 16 files changed, 554 insertions(+), 479 deletions(-)

diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
index e6afa38..db4f559 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
+++ b/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:
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py
index 56ab2e5..f08632f 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py
+++ b/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:
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py
index 0d9b1e3..b4547db 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index fabb19b..5c81100 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py
index 5aba18f..b189e3e 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py
+++ b/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)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
index 42aa566..ad6f5fc 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
+++ b/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):
     ## 镜像战斗结束
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_ArenaBattle.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_ArenaBattle.py
index c71d235..5e9d276 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_ArenaBattle.py
+++ b/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
+#
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossChampionship.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossChampionship.py
index e9b8e42..bdc0472 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossChampionship.py
+++ b/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
+#
\ No newline at end of file
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossRealmPK.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossRealmPK.py
index 31247dc..5dea30f 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossRealmPK.py
+++ b/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
+#
\ No newline at end of file
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py
index bb16b47..ef2477c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py
+++ b/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
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerArena.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerArena.py
index 565b682..b6b3a52 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerArena.py
+++ b/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:
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
index 942d3bc..35b52f9 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
index 8213c7e..c9daacc 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
+++ b/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):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_EnterFB.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_EnterFB.py
index fb0caad..71c5577 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_EnterFB.py
+++ b/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
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py
index cb61794..591d97a 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py
+++ b/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():
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
index f0a87cb..7d2a050 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
+++ b/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 # 地图上次处理的分钟

--
Gitblit v1.8.0