From 860db4b0dc36c4bde60e0069c442446c80bd72b6 Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期二, 02 九月 2025 17:07:49 +0800
Subject: [PATCH] 129 【战斗】战斗系统-服务端(关卡boss改为B410请求战斗,支持多小队一次性完整战报;B420增加状态6代表结束并休息;)

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MainLevelBoss.py |  136 ++++++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py                            |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py                           |   72 ++---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py                              |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini                                              |    6 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MainLevel.py     |   46 +++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py                                |  448 ++++++++++++++--------------------
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py                             |   16 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                                         |    3 
 9 files changed, 409 insertions(+), 322 deletions(-)

diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
index e7514e6..65c8a1e 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -1564,9 +1564,9 @@
 RegType = 0
 RegisterPackCount = 3
 
-PacketCMD_1=
-PacketSubCMD_1=
-PacketCallFunc_1=
+PacketCMD_1=0xB4
+PacketSubCMD_1=0x10
+PacketCallFunc_1=OnTurnFight
 
 PacketCMD_2=0xB4
 PacketSubCMD_2=0x13
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
index 157ee4a..01ef967 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
@@ -25,19 +25,18 @@
 import NetPackCommon
 import PlayerControl
 import GameWorld
-import FBCommon
 import IpyGameDataPY
 import PlayerOnline
 import NPCCommon
 import ShareDefine
 import PyGameData
-import ItemControler
 import SkillCommon
 import BattleObj
 import TurnPassive
 import TurnSkill
 import TurnBuff
 import ObjPool
+import FBLogic
 
 import random
 import time
@@ -55,7 +54,8 @@
 FightState_FightEnd, # 3 战斗结束
 FightState_Award, # 4 结算奖励
 FightState_Over, # 5 结束状态,无特殊意义,仅代表所有处理结束了,与Start对应
-) = range(6)
+FightState_Rest, # 6 结束并休息,一般是前端主动退出并休息的
+) = range(7)
     
 class BatLineup():
     ## 战斗阵容
@@ -149,6 +149,7 @@
         self.turnMax = 15 # 最大回合数
         self.enterLogic = False # 是否已执行进场逻辑
         self.winFaction = 0 # 本场战斗结束标记,获胜阵营,为0时代表未结束,所有小队打完或失败才有结果,0-未结束,>0-获胜的阵营
+        self.isWin = False
         self.batBuffer = "" # 战报buffer,战报暂时只保留最后一个小队的
         self.isNeedReport = isNeedReport # 是否需要战报
         self.msgDict = {} # 扩展信息字典,一般由MapID绑定的功能决定信息内容  {k:v, ...}
@@ -159,29 +160,49 @@
         self.timeline = 0 # 时间轴节点  turnNum*1000+actionIndex*100++actionNum
         self.startTime = 0 # 开始时间戳,支持毫秒小数
         self.costTime = 0 # 单场战斗总耗时,支持毫秒小数
+        
+        # 多小队 - 一般只有PVE用到
+        self.lineupIndex = 0 # 当前小队索引
+        self.lineupIDList = [] # npc小队列表
+        self.strongerLV = 0 # npc成长等级
+        self.difficulty = 0 # npc难度
         return
     
-    def setTurn(self, mapID, funcLineID, turnMax, isNeedReport=False, msgDict={}):
+    def setTurnFight(self, mapID, funcLineID, turnMax, isNeedReport=False, msgDict={}, lineupIDList=[], strongerLV=0, difficulty=0):
         ## 设置本场回合战斗设定
         self.mapID = mapID
         self.funcLineID = funcLineID
         self.turnMax = turnMax # 最大回合数
         self.isNeedReport = isNeedReport
+        self.lineupIndex = 0
+        self.lineupIDList = lineupIDList
+        self.strongerLV = strongerLV
+        self.difficulty = difficulty
         self.msgDict = {}
-        self.resetTurn(msgDict)
+        self.nextTurnFight(msgDict)
         return
     
-    def resetTurn(self, msgDict):
+    def nextTurnFight(self, msgDict={}):
         ## 一般用于玩家发起的战斗,在需要保留玩家阵容属性及状态的情况下,重置回合进入下一场战斗
         self.turnNum = 1
         self.enterLogic = False
         self.winFaction = 0
-        self.batBuffer = "" # 战报buffer
+        self.isWin = False
         self.msgDict.update(msgDict)
         self.timeline = 0
         self.startTime = time.time()
         self.costTime = 0
         return
+    
+    def haveNextLineup(self):
+        ## 是否可以打下一小队,获胜且还有下一小队时可打
+        return (self.winFaction == ChConfig.Def_FactionA and self.lineupIndex < len(self.lineupIDList) - 1)
+    def nextLineupID(self):
+        ## 下一小队ID
+        if not self.haveNextLineup():
+            return 0
+        self.lineupIndex += 1
+        return self.lineupIDList[self.lineupIndex]
     
     def setFactionLineup(self, faction, lineupDict):
         ## 设置阵营阵容
@@ -272,9 +293,9 @@
             
         return 0
     
-    def exitFight(self):
+    def exitFight(self, rest=False):
         ## 退出战斗
-        self.syncState(FightState_Over)
+        self.syncState(FightState_Over if not rest else FightState_Rest)
         for batFaction in self.factionDict.values():
             batFaction.clearLineups()
         self.state = -1
@@ -370,14 +391,18 @@
             headStr = "%02x%02x" % (clientPack.Head.Cmd, clientPack.Head.SubCmd)
         else:
             headStr = "%02x%02x" % (clientPack.Cmd, clientPack.SubCmd)
-        GameWorld.DebugLog("回合战斗过程封包: %s, isNeedReport=%s,%s" % (headStr, self.isNeedReport, self.guid))
         if self.isNeedReport:
-            self.batBuffer += clientPack.GetBuffer()
-        # 有玩家的统一每个包单独发送,同样也支持战报统计
-        if self.curPlayer:
-            NetPackCommon.SendFakePack(self.curPlayer, clientPack)
-        else:
+            packBuff = clientPack.GetBuffer()
+            GameWorld.DebugLog("回合战斗过程封包: %s" % (headStr))
+            self.batBuffer += packBuff
             ObjPool.GetPoolMgr().release(clientPack)
+        else:
+            GameWorld.DebugLog("回合战斗过程封包: %s" % (headStr))
+            # 有玩家的统一每个包单独发送,同样也支持战报统计
+            if self.curPlayer:
+                NetPackCommon.SendFakePack(self.curPlayer, clientPack)
+            else:
+                ObjPool.GetPoolMgr().release(clientPack)
         return
     
 class TurnFightMgr():
@@ -387,8 +412,10 @@
         self.turnFightDict = {} # {guid:TurnFight, ...}
         return
     
-    def addTurnFight(self, mapID, funcLineID=0, playerID=0):
-        tf = TurnFight(mapID, funcLineID, playerID, False)
+    def addTurnFight(self, mapID, funcLineID=0, playerID=0, isNeedReport=False):
+        tf = ObjPool.GetPoolMgr().acquire(TurnFight, mapID, funcLineID, playerID, isNeedReport)
+        if not tf:
+            tf = TurnFight(mapID, funcLineID, playerID, isNeedReport) # 一般是不可能,为了点出代码
         self.turnFightDict[tf.guid] = tf
         return tf
     
@@ -398,6 +425,7 @@
             return
         turnFight.exitFight()
         self.turnFightDict.pop(guid, None)
+        ObjPool.GetPoolMgr().release(turnFight)
         return
     
     def getTurnFight(self, guid):
@@ -426,12 +454,6 @@
         self.levelNum = 0 # 关卡编号
         self.waveMax = 6 # 本关最大波数,每波有多个小队,每个小队即为一张战斗 TurnFight
         self.wave = 0 # 当前刷怪波,注意不是玩家当前进度波,比如被击杀会回退一波
-        self.teamNum = 1 # 当前小队
-        self.teamMax = 1 # 当前波最大小队,某场战斗可能包含多个小队,所有小队混流击杀完才算过了本波
-        self.nextTeam = False # 下次前端请求战斗是否是下一小队,否则都是重新刷新当前进度怪
-        self.waveLineupList = [] # 小队列表
-        self.strongerLV = 0
-        self.difficulty = 0
         self.turnFight = GetTurnFightMgr().addTurnFight(ChConfig.Def_FBMapID_Main, 0, playerID)
         return
     
@@ -466,6 +488,8 @@
     
     playerID = curPlayer.GetPlayerID()
     lineup = PlayerOnline.GetOnlinePlayer(curPlayer).GetLineup(lineupID)
+    if not lineup.lineupHeroDict:
+        return {}
     
     lineupInfo = {"PlayerID":playerID, "FightPower":lineup.fightPower, "ShapeType":lineup.shapeType}
     heroDict = {}
@@ -731,12 +755,107 @@
         
     return
 
+#// B4 10 回合制战斗 #tagCMTurnFight
+#
+#struct    tagCMTurnFight
+#{
+#    tagHead        Head;
+#    DWORD        MapID;        // 自定义地图ID,可用于绑定战斗地图场景功能(如主线boss、爬塔、竞技场等)
+#    DWORD        FuncLineID;    // MapID对应的扩展值,如具体某个关卡等
+#    BYTE        TagType;        // 目标类型,0-NPC阵容,1-玩家
+#    DWORD        TagID;        // 目标类型对应的ID,如玩家ID
+#    BYTE        ValueCount;
+#    DWORD        ValueList[ValueCount]; // 附加值列表,可选,具体含义由MapID决定
+#};
+def OnTurnFight(index, clientData, tick):
+    ''' 本战斗会一次性处理完所有小队战斗,以战报的形式下发,目前除了主线小怪分段战斗外,均使用该战斗
+    '''
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    mapID = clientData.MapID
+    funcLineID = clientData.FuncLineID
+    tagType = clientData.TagType
+    tagID = clientData.TagID
+    valueList = clientData.ValueList
+    
+    GameWorld.DebugLog("回合制战斗请求: mapID=%s,funcLineID=%s,tagType=%s,tagID=%s,valueList=%s" 
+                       % (mapID, funcLineID, tagType, tagID, valueList), curPlayer.GetPlayerID())
+    
+    reqRet = FBLogic.OnTurnFightRequest(curPlayer, mapID, funcLineID, tagType, tagID, valueList)
+    if not reqRet:
+        return
+    
+    # 攻防方所使用的阵容ID
+    atkLineupID, defLineupID = FBLogic.GetFBPlayerLineupID(curPlayer, mapID, funcLineID)
+    if atkLineupID not in ShareDefine.LineupList or defLineupID not in ShareDefine.LineupList:
+        return
+        
+    # 玩家
+    if tagType == 1:
+        # OnTurnFightVSPlayer
+        pass
+    
+    # NPC
+    else:
+        ret = FBLogic.GetFBNPCLineupInfo(curPlayer, mapID, funcLineID)
+        if not ret:
+            return
+        npcLineupIDList, strongerLV, difficulty = ret
+        OnTurnFightVSNPC(curPlayer, mapID, funcLineID, atkLineupID, npcLineupIDList, strongerLV, difficulty)
+        
+    return
+
+def OnTurnFightVSNPC(curPlayer, mapID, funcLineID, atkLineupID, npcLineupIDList, strongerLV, difficulty):
+    playerID = curPlayer.GetPlayerID()
+    GameWorld.DebugLog("OnTurnFightVSNPC: mapID=%s,funcLineID=%s,atkLineupID=%s,npcLineupIDList=%s,strongerLV=%s,difficulty=%s" 
+                       % (mapID, funcLineID, atkLineupID, npcLineupIDList, strongerLV, difficulty), playerID)
+    if not npcLineupIDList:
+        return
+    
+    lineupMainInfo = GetPlayerLineupInfo(curPlayer, atkLineupID)
+    if not lineupMainInfo:
+        GameWorld.DebugLog("玩家没有该阵容数据! atkLineupID=%s" % atkLineupID, playerID)
+        return
+    
+    turnMax = 15
+    if mapID == ChConfig.Def_FBMapID_MainBoss:
+        turnMax = IpyGameDataPY.GetFuncCfg("Mainline", 3)
+        # 停止主线小怪战斗、清空
+        mainFightMgr = GetMainFightMgr(curPlayer)
+        mainTF = mainFightMgr.turnFight
+        if mainTF.isInFight():
+            mainTF.exitFight()
+            
+    tfMgr = GetTurnFightMgr()
+    turnFight = tfMgr.addTurnFight(mapID, funcLineID, playerID)
+    guid = turnFight.guid
+    
+    turnFight.setTurnFight(mapID, funcLineID, turnMax, True, lineupIDList=npcLineupIDList, strongerLV=strongerLV, difficulty=difficulty)
+    turnFight.setFactionLineup(ChConfig.Def_FactionA, {1:lineupMainInfo})
+    
+    for index, lineupID in enumerate(npcLineupIDList):
+        turnFight.lineupIndex = index
+        GameWorld.DebugLog("对战NPC阵容: index=%s, lineupID=%s" % (index, lineupID))
+        if index > 0:
+            turnFight.nextTurnFight()
+        turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(lineupID, strongerLV, difficulty)})
+        turnFight.sortActionQueue()
+        turnFight.startFight()
+        
+        __processTurnFight(turnFight.guid)
+        
+        if not turnFight.isWin:
+            break
+        
+    SyncTurnFightReport(curPlayer, guid, turnFight.batBuffer)
+    tfMgr.delTurnFight(guid)
+    return
+
 #// B4 13 主线战斗请求 #tagCSMainFightReq
 #
 #struct    tagCSMainFightReq
 #{
 #    tagHead        Head;
-#    BYTE        ReqType;        // 0-停止战斗回城;1-设置消耗倍值;2-挑战关卡小怪;3-挑战关卡boss;4-继续战斗;
+#    BYTE        ReqType;        // 0-停止战斗回城;1-设置消耗倍值;2-挑战关卡小怪;4-继续战斗;
 #    DWORD        ReqValue;    // 请求值,ReqType为1时发送消耗倍值
 #};
 def OnMainFightReq(index, clientData, tick):
@@ -758,8 +877,6 @@
     
     if reqType == 2: # 前端主动请求开始关卡小怪的视为从休息中开始
         __doMainLevelWave(curPlayer, True)
-    elif reqType == 3:
-        __doMainBossStart(curPlayer)
     elif reqType == 4:
         __doMainFight(curPlayer, tick)
     else:
@@ -775,7 +892,7 @@
     mainFightMgr = GetMainFightMgr(curPlayer)
     turnFight = mainFightMgr.turnFight
     if turnFight:
-        turnFight.exitFight()
+        turnFight.exitFight(True)
     return
 
 def __doSetFightPoint(curPlayer, fightPoint):
@@ -799,9 +916,6 @@
     # @param isRestStart: 是否从休息状态重新开始的
     playerID = curPlayer.GetPlayerID()
     chapterID, levelNum, wave = PlayerControl.GetMainLevelNowInfo(curPlayer)
-    if not chapterID and not levelNum:
-        PlayerControl.SetMainLevelNowInfo(curPlayer, 1, 1, 1)
-        chapterID, levelNum, wave = PlayerControl.GetMainLevelNowInfo(curPlayer)
     GameWorld.DebugLog("请求关卡波战斗: chapterID=%s,levelNum=%s,wave=%s,isRestStart=%s" % (chapterID, levelNum, wave, isRestStart), playerID)
     fightPoint = max(curPlayer.GetFightPoint(), 1)
     if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
@@ -830,12 +944,10 @@
     if wave > waveMax:
         wave = waveMax
         
-    waveLineupList = getattr(levelIpyData, "GetWaveLineupIDList%s" % wave)() # 小队1阵容ID|小队2阵容ID|...
-    if not waveLineupList:
+    lineupIDList = getattr(levelIpyData, "GetWaveLineupIDList%s" % wave)() # 小队1阵容ID|小队2阵容ID|...
+    if not lineupIDList:
         return
-    teamMax = len(waveLineupList)
-    teamNum = 1
-    lineupID = waveLineupList[teamNum - 1] # NPC阵容ID
+    lineupID = lineupIDList[0] # NPC阵容ID
     
     lineupMainInfo = GetPlayerLineupInfo(curPlayer, ShareDefine.Lineup_Main)
     if not lineupMainInfo:
@@ -845,110 +957,23 @@
     strongerLV = levelIpyData.GetNPCLV()
     difficulty = levelIpyData.GetDifficulty()
     mainFightMgr = GetMainFightMgr(curPlayer)
-    mainFightMgr.nextTeam = False
     mainFightMgr.chapterID = chapterID
     mainFightMgr.levelNum = levelNum
     mainFightMgr.waveMax = waveMax
     mainFightMgr.wave = wave
-    mainFightMgr.waveLineupList = waveLineupList
-    mainFightMgr.teamNum = teamNum
-    mainFightMgr.teamMax = teamMax
-    mainFightMgr.strongerLV = strongerLV
-    mainFightMgr.difficulty = difficulty
     turnMax = IpyGameDataPY.GetFuncCfg("Mainline", 2)
     mapID, funcLineID = ChConfig.Def_FBMapID_Main, PlayerControl.ComMainLevelValue(chapterID, levelNum, wave)
-    GameWorld.DebugLog("设置起始关卡波: 关卡%s-%s,波=%s/%s,teamMax=%s,mapID=%s,funcLineID=%s,lineupID=%s,strongerLV=%s,difficulty=%s" 
-                       % (chapterID, levelNum, wave, waveMax, teamMax, mapID, funcLineID, lineupID, strongerLV, difficulty), playerID)
+    GameWorld.DebugLog("设置起始关卡波: 关卡%s-%s,波=%s/%s,lineupIDList=%s,mapID=%s,funcLineID=%s,lineupID=%s,strongerLV=%s,difficulty=%s" 
+                       % (chapterID, levelNum, wave, waveMax, lineupIDList, mapID, funcLineID, lineupID, strongerLV, difficulty), playerID)
     
     turnFight = mainFightMgr.turnFight
-    turnFight.setTurn(mapID, funcLineID, turnMax, False, {"teamNum":teamNum, "teamMax":teamMax})
+    turnFight.setTurnFight(mapID, funcLineID, turnMax, False, lineupIDList=lineupIDList, strongerLV=strongerLV, difficulty=difficulty)
     turnFight.setFactionLineup(ChConfig.Def_FactionA, {1:lineupMainInfo})
     turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(lineupID, strongerLV, difficulty)})
     turnFight.sortActionQueue()
     turnFight.startFight()
     
     __doMainFight(curPlayer)
-    return
-
-def __doMainBossStart(curPlayer):
-    ## 开始挑战关卡boss
-    playerID = curPlayer.GetPlayerID()
-    chapterID, levelNum, wave = PlayerControl.GetMainLevelPassInfo(curPlayer)
-    if not chapterID:
-        chapterID = 1
-    if not levelNum:
-        levelNum = 1
-    GameWorld.DebugLog("请求挑战关卡Boss! passInfo: chapterID=%s,levelNum=%s,wave=%s" % (chapterID, levelNum, wave), playerID)
-    chapterIpyData = IpyGameDataPY.GetIpyGameData("MainChapter", chapterID)
-    if not chapterIpyData:
-        return
-    levelIpyData = IpyGameDataPY.GetIpyGameData("MainLevel", chapterID, levelNum)
-    if not levelIpyData:
-        return
-    
-    nowChapterID, nowLevelNum, _ = PlayerControl.GetMainLevelNowInfo(curPlayer)
-    if chapterID != nowChapterID or levelNum != nowLevelNum:
-        '''一般有两种情况会导致
-        1. 理论上玩家当前刷怪的章节关卡一定是与过关的章节关卡是一致的,除了全部章节已通关
-                                当通关时,已过关的记录会被设置为下一章节第1关(版本关卡表实际不存在该关卡)
-                                但是当前刷怪的还是版本的最后一关,固会出现不一致的情况
-                                在增加章节关卡新版本更新后玩家重登会自动进行修复当前刷怪进度为最新章节第1关
-        2. 真的出现问题了,可能是bug引起,这种情况只能进行bug修复及针对异常账号特殊处理,或者重登后也会自动进行修正
-        '''
-        GameWorld.ErrLog("当前刷怪章节关卡与挑战的boss章节关卡不一致,无法挑战! chapterID=%s,levelNum=%s,nowChapterID=%s,nowLevelNum=%s" 
-                         % (chapterID, levelNum, nowChapterID, nowLevelNum), playerID)
-        return
-    
-    # 本关卡最大波数
-    waveMax = 6
-    while waveMax >= 1 and (not hasattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax) or not getattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax)()):
-        waveMax -= 1
-        
-    if wave < waveMax:
-        GameWorld.DebugLog("最后一波未通过,无法挑战本关boss! passWave=%s < %s" % (wave, waveMax))
-        return
-    
-    waveLineupList = levelIpyData.GetBossLineupIDList() # Boss波阵容ID列表,小队1阵容ID|小队2阵容ID|...
-    if not waveLineupList:
-        return
-    teamMax = len(waveLineupList)
-    teamNum = 1
-    lineupID = waveLineupList[teamNum - 1] # NPC阵容ID
-    
-    wave = waveMax = 1 # 关卡boss固定只有一波
-    
-    lineupMainInfo = GetPlayerLineupInfo(curPlayer, ShareDefine.Lineup_Main)
-    if not lineupMainInfo:
-        GameWorld.DebugLog("没有设置主阵容!", playerID)
-        return
-        
-    strongerLV = levelIpyData.GetNPCLV()
-    difficulty = levelIpyData.GetDifficulty()
-    mainFightMgr = GetMainFightMgr(curPlayer)
-    mainFightMgr.nextTeam = False
-    mainFightMgr.chapterID = chapterID
-    mainFightMgr.levelNum = levelNum
-    mainFightMgr.waveMax = waveMax
-    mainFightMgr.wave = wave
-    mainFightMgr.waveLineupList = waveLineupList
-    mainFightMgr.teamNum = teamNum
-    mainFightMgr.teamMax = teamMax
-    mainFightMgr.strongerLV = strongerLV
-    mainFightMgr.difficulty = difficulty
-    turnMax = IpyGameDataPY.GetFuncCfg("Mainline", 3)
-    mapID, funcLineID = ChConfig.Def_FBMapID_MainBoss, PlayerControl.ComMainLevelValue(chapterID, levelNum, wave)
-    GameWorld.DebugLog("设置关卡boss: 关卡%s-%s,波=%s/%s,teamMax=%s,mapID=%s,funcLineID=%s,lineupID=%s,strongerLV=%s,difficulty=%s" 
-                       % (chapterID, levelNum, wave, waveMax, teamMax, mapID, funcLineID, lineupID, strongerLV, difficulty), playerID)
-    
-    turnFight = mainFightMgr.turnFight
-    turnFight.setTurn(mapID, funcLineID, turnMax, False, {"teamNum":teamNum, "teamMax":teamMax})
-    turnFight.setFactionLineup(ChConfig.Def_FactionA, {1:lineupMainInfo})
-    turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(lineupID, strongerLV, difficulty)})
-    turnFight.sortActionQueue()
-    turnFight.startFight()
-    
-    # 挑战boss无中间过程,每次执行直接挑战一队结果
-    __processTurnFight(turnFight.guid)
     return
 
 def __doMainFight(curPlayer, tick=0):
@@ -969,36 +994,25 @@
     turnFight = mainFightMgr.turnFight
     
     isLevelBoss = mainFightMgr.isLevelBoss()
+    if isLevelBoss:
+        ## 关卡boss是一次性处理完的,一般不可能走到这里,这边做下防范
+        return
+    
     winFaction = turnFight.winFaction
     if winFaction:
-        if mainFightMgr.nextTeam:
-            # 自动开始下一小队
-            teamNum = mainFightMgr.teamNum = mainFightMgr.teamNum + 1
-            GameWorld.DebugLog("开始进入下一小队: teamNum=%s" % teamNum)
-            if teamNum < 1 or teamNum > len(mainFightMgr.waveLineupList):
-                return
-            lineupID = mainFightMgr.waveLineupList[teamNum - 1] # NPC阵容ID
-            GameWorld.DebugLog("teamNum=%s,lineupID=%s" % (teamNum, lineupID))
+        nextLineupID = turnFight.nextLineupID()
+        if nextLineupID:
+            GameWorld.DebugLog("---开始进入下一小队: lineupIndex=%s,nextLineupID=%s,%s" % (turnFight.lineupIndex, nextLineupID, turnFight.npcLineupIDList))
             
-            mainFightMgr.nextTeam = False
-            turnFight.resetTurn({"teamNum":teamNum})
+            turnFight.nextTurnFight()
             # 切换小队时,玩家阵容不需要处理,保留状态
-            turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(lineupID, mainFightMgr.strongerLV, mainFightMgr.difficulty)})
+            turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(nextLineupID, turnFight.strongerLV, turnFight.difficulty)})
             turnFight.sortActionQueue()
             turnFight.startFight()
             
-            if mainFightMgr.isLevelBoss():
-                # 每次处理一小队的完整战斗,相当于一次完整战报
-                __processTurnFight(turnFight.guid)
-                return
-            else:
-                __doMainFight(curPlayer)
+            __doMainFight(curPlayer)
         else:
             __doMainLevelWave(curPlayer, False)
-        return
-    
-    if isLevelBoss:
-        ## 关卡boss是一次性处理完的,一般不可能走到这里,这边做下防范
         return
     
     # 小怪战斗,每次消耗1个战锤
@@ -1486,6 +1500,7 @@
     
     turnFight.costTime = time.time() - turnFight.startTime
     winFaction = turnFight.winFaction
+    turnFight.isWin = (winFaction == ChConfig.Def_FactionA)
     GameWorld.DebugLog("--- 战斗结束处理 ---, winFaction=%s, costTime=%ss, %s" % (winFaction, turnFight.costTime, guid))
     
     # 统计明细
@@ -1516,128 +1531,15 @@
                                    % (posNum, objID, npcID, heroID, batObj.GetHP(), batObj.GetMaxHP(), atkHurt, defHurt, cureHP))
                 lineupStatInfo[str(posNum)] = {"ObjID":objID, "HeroID":heroID, "NPCID":npcID, "AtkHurt":atkHurt, "DefHurt":defHurt, "CureHP":cureHP}
                 
-    awardItemList = []
-    playerID = turnFight.playerID
-    if playerID:
-        curPlayer = turnFight.curPlayer
-        isWin = (winFaction == ChConfig.Def_FactionA)
-        # 主线小怪
-        if turnFight.mapID == ChConfig.Def_FBMapID_Main:
-            OnOver_MainLevel(curPlayer, isWin, awardItemList)
-        # 主线boss
-        elif turnFight.mapID == ChConfig.Def_FBMapID_MainBoss:
-            OnOver_MainLevelBoss(curPlayer, isWin, awardItemList)    
-        else:
-            pass
-        
-    overMsg = {"winFaction":winFaction, "statInfo":statInfo, FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(awardItemList)}
+    overMsg = {"winFaction":winFaction, "statInfo":statInfo}
+    curPlayer = turnFight.curPlayer
+    mapID = turnFight.mapID
+    funcLineID = turnFight.funcLineID
+    
+    FBLogic.OnTurnFightOver(curPlayer, turnFight, mapID, funcLineID, overMsg)
+    
     turnFight.syncState(FightState_Award, overMsg)
-    
-    # 除了主线外,其余战斗结束后均直接删除
-    if not turnFight.playerID:
-        tfMgr.delTurnFight(guid)
     return
-
-def OnOver_MainLevel(curPlayer, isWin, awardItemList):
-    ## 战斗结束额外处理 - 主线关卡
-    if not curPlayer:
-        return
-    playerID = curPlayer.GetPlayerID()
-    mainFightMgr = GetMainFightMgr(curPlayer)
-    mainFightMgr.nextTeam = False
-    chapterID, levelNum, wave = PlayerControl.GetMainLevelNowInfo(curPlayer)
-    
-    if not isWin:
-        nextWave = max(1, wave - 1)
-        nowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, chapterID, levelNum, nextWave)
-        GameWorld.DebugLog("主线小怪战斗失败,降一波! chapterID=%s,levelNum=%s,wave=%s,nextWave=%s,nowValue=%s" 
-                           % (chapterID, levelNum, wave, nextWave, nowValue), playerID)
-        return
-    
-    if mainFightMgr.teamNum < mainFightMgr.teamMax:
-        mainFightMgr.nextTeam = True
-        GameWorld.DebugLog("主线小怪战斗胜利,下一小队! chapterID=%s,levelNum=%s,wave=%s,teamNum=%s/%s" 
-                           % (chapterID, levelNum, wave, mainFightMgr.teamNum, mainFightMgr.teamMax), playerID)
-        return
-    
-    # 获胜过波
-    if wave < mainFightMgr.waveMax:
-        nextWave = min(mainFightMgr.waveMax, wave + 1)
-        nowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, chapterID, levelNum, nextWave)
-        GameWorld.DebugLog("主线小怪波战斗胜利,下一波! chapterID=%s,levelNum=%s,wave=%s,nextWave=%s,nowValue=%s" 
-                           % (chapterID, levelNum, wave, nextWave, nowValue), playerID)
-    else:
-        GameWorld.DebugLog("主线小怪波战斗胜利,最后一波循环刷! chapterID=%s,levelNum=%s,wave=%s" % (chapterID, levelNum, wave), playerID)
-        
-    # 小怪可能会退波,所以只在有超过已过关卡进度时才更新值
-    hisPassValue = PlayerControl.GetMainLevelPassValue(curPlayer)
-    curPassValue = PlayerControl.ComMainLevelValue(chapterID, levelNum, wave)
-    if curPassValue > hisPassValue:
-        GameWorld.DebugLog("更新当前过关进度! curPassValue=%s,hisPassValue=%s" % (curPassValue, hisPassValue), playerID)
-        PlayerControl.SetMainLevelPassValue(curPlayer, curPassValue)
-    else:
-        GameWorld.DebugLog("未超过当前过关进度,不更新! curPassValue=%s <= hisPassValue=%s" % (curPassValue, hisPassValue), playerID)
-        
-    return
-
-def OnOver_MainLevelBoss(curPlayer, isWin, awardItemList):
-    ## 战斗结束额外处理 - 主线关卡boss
-    if not curPlayer:
-        return
-    playerID = curPlayer.GetPlayerID()
-    mainFightMgr = GetMainFightMgr(curPlayer)
-    mainFightMgr.nextTeam = False
-    chapterID, levelNum, _ = PlayerControl.GetMainLevelPassInfo(curPlayer)
-    if not isWin:
-        nowValue = PlayerControl.GetMainLevelNowValue(curPlayer)
-        GameWorld.DebugLog("主线boss战斗失败!保持当前刷怪波进度不变! nowValue=%s" % nowValue, playerID)
-        return
-    
-    if mainFightMgr.teamNum < mainFightMgr.teamMax:
-        mainFightMgr.nextTeam = True
-        GameWorld.DebugLog("主线boss小队战斗胜利,下一小队! chapterID=%s,levelNum=%s,teamNum=%s/%s" 
-                           % (chapterID, levelNum, mainFightMgr.teamNum, mainFightMgr.teamMax), playerID)
-        return
-    
-    isAllPass = False # 是否通关
-    if IpyGameDataPY.GetIpyGameDataNotLog("MainLevel", chapterID, levelNum + 1):
-        nextChapterID, nextLevelNum = chapterID, levelNum + 1
-        GameWorld.DebugLog("主线boss波战斗胜利!下一关! chapterID=%s,levelNum=%s,nextLevelNum=%s" 
-                           % (chapterID, levelNum, nextLevelNum), playerID)
-    elif IpyGameDataPY.GetIpyGameDataNotLog("MainLevel", chapterID + 1, 1):
-        nextChapterID, nextLevelNum = chapterID + 1, 1
-        GameWorld.DebugLog("主线boss波战斗胜利!下一章! chapterID=%s,levelNum=%s,nextChapterID=%s,nextLevelNum=%s" 
-                           % (chapterID, levelNum, nextChapterID, nextLevelNum), playerID)
-    else:
-        # 已通关的暂时先保持不变
-        # 注意防范最后一关奖励重复获得
-        nextChapterID, nextLevelNum = chapterID + 1, 1
-        GameWorld.DebugLog("主线boss波战斗胜利!已通关! chapterID=%s,levelNum=%s,nextChapterID=%s,nextLevelNum=%s" 
-                            % (chapterID, levelNum, nextChapterID, nextLevelNum), playerID)
-        isAllPass = True
-        
-    updPassValue = PlayerControl.SetMainLevelPassInfo(curPlayer, nextChapterID, nextLevelNum, 0)
-    if isAllPass:
-        # 已通关的刷怪进度保持不变
-        updNowValue = PlayerControl.GetMainLevelNowValue(curPlayer)
-        GameWorld.DebugLog("已通关的刷怪进度保持不变: updNowValue=%s" % updNowValue, playerID)
-    else:
-        updNowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, nextChapterID, nextLevelNum, 1)
-        GameWorld.DebugLog("为通关的刷怪进度设置为下一关的第1波: updNowValue=%s" % updNowValue, playerID)
-    GameWorld.DebugLog("updPassValue=%s,updNowValue=%s" % (updPassValue, updNowValue), playerID)
-    
-    # 发放过关奖励
-    levelIpyData = IpyGameDataPY.GetIpyGameData("MainLevel", chapterID, levelNum)
-    if not levelIpyData:
-        return
-    itemList = levelIpyData.GetAwardItemList()
-    GameWorld.DebugLog("过关奖励: chapterID=%s,levelNum=%s,itemList=%s" % (chapterID, levelNum, itemList), playerID)
-    
-    ItemControler.GivePlayerItemOrMail(curPlayer, itemList, event=["MainLevelBoss", False, {}], isNotifyAward=False)
-    
-    awardItemList += itemList
-    return
-
 
 #// B4 14 查看战报 #tagCSTurnFightReportView
 #
@@ -1647,4 +1549,18 @@
 #    char        GUID[40];    //战报guid
 #};
 def OnTurnFightReportView(index, clientData, tick):
+    
+    # 通过查找已存在的战报
+    
+    #SyncTurnFightReport(curPlayer, guid, reprot)
     return
+
+def SyncTurnFightReport(curPlayer, guid, reprot):
+    ## 通知完整战报
+    clientPack = ChPyNetSendPack.tagSCTurnFightReport()
+    clientPack.GUID = guid
+    clientPack.Report = reprot
+    clientPack.Len = len(clientPack.Report)
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return
+    
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index 75990e2..12deb63 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -2111,7 +2111,8 @@
 
 #副本ID转换
 Def_FB_MapID = {
-                'MainLevel':[Def_FBMapID_Main, Def_FBMapID_MainBoss],  # 主线关卡
+                'MainLevel':[Def_FBMapID_Main],  # 主线关卡
+                'MainLevelBoss':[Def_FBMapID_MainBoss],  # 主线关卡boss
                 }
 
 #特殊副本ID, 由系统分配, 进入时候不验证IsMapCopyFull
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py
index 64bc3ad..62016fd 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/MainLevel.py
@@ -67,7 +67,7 @@
     
     if value <= 0:
         nowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, 1, 1, 1)
-        passValue = PlayerControl.SetMainLevelPassInfo(curPlayer, 0, 0, 0)
+        passValue = PlayerControl.SetMainLevelPassInfo(curPlayer, 1, 1, 0)
         GameWorld.DebugAnswer(curPlayer, "重置主线:now=%s,pass=%s" % (nowValue, passValue))
         return
     
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
index 0edfc95..8aa2f91 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
@@ -2363,14 +2363,6 @@
     
     return callFunc(curPlayer, mapID, lineID)
 
-def OnPlayerLVUp(curPlayer):
-    ## 玩家升级
-    do_FBLogic_ID = __GetFBLogic_MapID(GameWorld.GetMap().GetMapID())
-    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnPlayerLVUp"))
-    if callFunc == None:
-        return False
-    return callFunc(curPlayer)
-
 def OnTurnFightRequest(curPlayer, mapID, funcLineID, tagType, tagID, valueList):
     ## 回合战斗请求 - 地图验证
     # @return: 是否允许
@@ -2384,6 +2376,30 @@
     
     return callFunc(curPlayer, mapID, funcLineID, tagType, tagID, valueList)
 
+def GetFBPlayerLineupID(curPlayer, mapID, funcLineID):
+    ## 获取玩家使用的攻防阵容ID
+    # @return: 攻击方阵容ID, 防守方阵容ID
+    do_FBLogic_ID = __GetFBLogic_MapID(mapID)
+    
+    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "GetFBPlayerLineupID"))
+    
+    if callFunc == None:
+        # 默认不限制
+        return ShareDefine.Lineup_Main, ShareDefine.Lineup_Main
+    
+    return callFunc(curPlayer, mapID, funcLineID)
+
+def GetFBNPCLineupInfo(curPlayer, mapID, funcLineID):
+    ## 获取NPC阵容相关
+    # @return: npcLineupIDList, strongerLV, difficulty
+    do_FBLogic_ID = __GetFBLogic_MapID(mapID)
+    
+    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "GetFBNPCLineupInfo"))
+    if callFunc == None:
+        return
+    
+    return callFunc(curPlayer, mapID, funcLineID)
+
 def OnPlayerLineupAttackResult(curPlayer, atkObj, killObjIDList, useSkill, mapID, funcLineID):
     ## 回合战斗主动发起的玩家阵容攻击结果额外处理 ,一般处理副本相关的掉落、奖励等
     do_FBLogic_ID = __GetFBLogic_MapID(mapID)
@@ -2395,9 +2411,8 @@
         
     return
 
-def OnTurnFightOver(curPlayer, mapID, funcLineID, tagType, tagID, valueList, fightRet):
+def OnTurnFightOver(curPlayer, turnFight, mapID, funcLineID, overMsg):
     ## 回合战斗结束
-    # @return: 是否需要同步GameServer, 奖励列表
     
     do_FBLogic_ID = __GetFBLogic_MapID(mapID)
     
@@ -2406,40 +2421,5 @@
     if callFunc == None:
         return
     
-    return callFunc(curPlayer, mapID, funcLineID, tagType, tagID, valueList, fightRet)
+    return callFunc(curPlayer, turnFight, mapID, funcLineID, overMsg)
 
-def OnTurnFightOver_GameServerRet(curPlayer, mapID, funcLineID, tagType, tagID, valueList, fightRet, awardItemList, ret):
-    ## 回合战斗结束 - GameServer处理完毕返回
-    
-    do_FBLogic_ID = __GetFBLogic_MapID(mapID)
-    
-    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnTurnFightOver_GameServerRet"))
-    
-    if callFunc == None:
-        return
-    
-    return callFunc(curPlayer, mapID, funcLineID, tagType, tagID, valueList, fightRet, awardItemList, ret)
-
-def OnMirrorBattleRequest(curPlayer, mapID, funcLineID, factionPlayerList):
-    ## 镜像战斗请求
-    do_FBLogic_ID = __GetFBLogic_MapID(mapID)
-    
-    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnMirrorBattleRequest"))
-    
-    if callFunc == None:
-        # 默认允许
-        return True
-    
-    return callFunc(curPlayer, mapID, funcLineID, factionPlayerList)
-
-def OnMirrorBattleOver(battleID, mapID):
-    ## 镜像战斗结束
-    
-    do_FBLogic_ID = __GetFBLogic_MapID(mapID)
-    
-    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnMirrorBattleOver"))
-    
-    if callFunc == None:
-        return
-    
-    return callFunc(battleID)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MainLevel.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MainLevel.py
index fba8115..cef5c8b 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MainLevel.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MainLevel.py
@@ -17,6 +17,7 @@
 
 import ChConfig
 import GameWorld
+import TurnAttack
 import ShareDefine
 import IpyGameDataPY
 import PlayerControl
@@ -408,3 +409,48 @@
     clientPack.Count = len(clientPack.DropBootyList)
     NetPackCommon.SendFakePack(curPlayer, clientPack)
     return
+
+def OnTurnFightOver(curPlayer, turnFight, mapID, funcLineID, overMsg):
+    ## 回合战斗结束
+    
+    if not curPlayer:
+        return
+    
+    #winFaction = turnFight.winFaction
+    isWin = turnFight.isWin
+    
+    playerID = curPlayer.GetPlayerID()
+    mainFightMgr = TurnAttack.GetMainFightMgr(curPlayer)
+    chapterID, levelNum, wave = PlayerControl.GetMainLevelNowInfo(curPlayer)
+    
+    if not isWin:
+        nextWave = max(1, wave - 1)
+        nowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, chapterID, levelNum, nextWave)
+        GameWorld.DebugLog("主线小怪战斗失败,降一波! chapterID=%s,levelNum=%s,wave=%s,nextWave=%s,nowValue=%s" 
+                           % (chapterID, levelNum, wave, nextWave, nowValue), playerID)
+        return
+    
+    if turnFight.haveNextLineup():
+        GameWorld.DebugLog("主线小怪战斗胜利,有下一小队! chapterID=%s,levelNum=%s,wave=%s" % (chapterID, levelNum, wave), playerID)
+        return
+    
+    # 获胜过波
+    if wave < mainFightMgr.waveMax:
+        nextWave = min(mainFightMgr.waveMax, wave + 1)
+        nowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, chapterID, levelNum, nextWave)
+        GameWorld.DebugLog("主线小怪波战斗胜利,下一波! chapterID=%s,levelNum=%s,wave=%s,nextWave=%s,nowValue=%s" 
+                           % (chapterID, levelNum, wave, nextWave, nowValue), playerID)
+    else:
+        GameWorld.DebugLog("主线小怪波战斗胜利,最后一波循环刷! chapterID=%s,levelNum=%s,wave=%s" % (chapterID, levelNum, wave), playerID)
+        
+    # 小怪可能会退波,所以只在有超过已过关卡进度时才更新值
+    hisPassValue = PlayerControl.GetMainLevelPassValue(curPlayer)
+    curPassValue = PlayerControl.ComMainLevelValue(chapterID, levelNum, wave)
+    if curPassValue > hisPassValue:
+        GameWorld.DebugLog("更新当前过关进度! curPassValue=%s,hisPassValue=%s" % (curPassValue, hisPassValue), playerID)
+        PlayerControl.SetMainLevelPassValue(curPlayer, curPassValue)
+    else:
+        GameWorld.DebugLog("未超过当前过关进度,不更新! curPassValue=%s <= hisPassValue=%s" % (curPassValue, hisPassValue), playerID)
+        
+    return
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MainLevelBoss.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MainLevelBoss.py
new file mode 100644
index 0000000..9f205d3
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MainLevelBoss.py
@@ -0,0 +1,136 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package GameWorldLogic.FBProcess.GameLogic_MainLevelBoss
+#
+# @todo:主线关卡boss
+# @author hxp
+# @date 2025-9-2
+# @version 1.0
+#
+# 详细描述: 主线关卡boss
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2025-9-2 下午5:07:12"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import IpyGameDataPY
+import PlayerControl
+import ItemControler
+
+import FBCommon
+
+def OnTurnFightRequest(curPlayer, mapID, funcLineID, tagType, tagID, valueList):
+    ## 回合战斗请求 
+    playerID = curPlayer.GetPlayerID()
+    chapterID, levelNum, wave = PlayerControl.GetMainLevelPassInfo(curPlayer)
+    #levelID = chapterID * 100 + levelNum
+    #if funcLineID != levelID:
+    #    return
+    GameWorld.DebugLog("请求挑战关卡Boss! passInfo: chapterID=%s,levelNum=%s,wave=%s" % (chapterID, levelNum, wave), playerID)
+    chapterIpyData = IpyGameDataPY.GetIpyGameData("MainChapter", chapterID)
+    if not chapterIpyData:
+        return
+    levelIpyData = IpyGameDataPY.GetIpyGameData("MainLevel", chapterID, levelNum)
+    if not levelIpyData:
+        return
+    
+    nowChapterID, nowLevelNum, _ = PlayerControl.GetMainLevelNowInfo(curPlayer)
+    if chapterID != nowChapterID or levelNum != nowLevelNum:
+        '''一般有两种情况会导致
+        1. 理论上玩家当前刷怪的章节关卡一定是与过关的章节关卡是一致的,除了全部章节已通关
+                                当通关时,已过关的记录会被设置为下一章节第1关(版本关卡表实际不存在该关卡)
+                                但是当前刷怪的还是版本的最后一关,固会出现不一致的情况
+                                在增加章节关卡新版本更新后玩家重登会自动进行修复当前刷怪进度为最新章节第1关
+        2. 真的出现问题了,可能是bug引起,这种情况只能进行bug修复及针对异常账号特殊处理,或者重登后也会自动进行修正
+        '''
+        GameWorld.ErrLog("当前刷怪章节关卡与挑战的boss章节关卡不一致,无法挑战! chapterID=%s,levelNum=%s,nowChapterID=%s,nowLevelNum=%s" 
+                         % (chapterID, levelNum, nowChapterID, nowLevelNum), playerID)
+        return
+    
+    # 本关卡最大波数
+    waveMax = 6
+    while waveMax >= 1 and (not hasattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax) or not getattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax)()):
+        waveMax -= 1
+        
+    if wave < waveMax:
+        GameWorld.DebugLog("最后一波未通过,无法挑战本关boss! passWave=%s < %s" % (wave, waveMax))
+        return
+    
+    return True
+
+def GetFBNPCLineupInfo(curPlayer, mapID, funcLineID):
+    ## 获取NPC阵容相关
+    # @return: npcLineupIDList, strongerLV, difficulty
+    
+    chapterID, levelNum, _ = PlayerControl.GetMainLevelPassInfo(curPlayer)
+    levelIpyData = IpyGameDataPY.GetIpyGameData("MainLevel", chapterID, levelNum)
+    if not levelIpyData:
+        return
+    
+    npcLineupIDList = levelIpyData.GetBossLineupIDList() # Boss波阵容ID列表,小队1阵容ID|小队2阵容ID|...
+    strongerLV = levelIpyData.GetNPCLV()
+    difficulty = levelIpyData.GetDifficulty()
+    
+    return npcLineupIDList, strongerLV, difficulty
+
+def OnTurnFightOver(curPlayer, turnFight, mapID, funcLineID, overMsg):
+    ## 回合战斗结束
+    if not curPlayer:
+        return
+    
+    playerID = curPlayer.GetPlayerID()
+    #winFaction = turnFight.winFaction
+    isWin = turnFight.isWin
+    
+    chapterID, levelNum, _ = PlayerControl.GetMainLevelPassInfo(curPlayer)
+    if not isWin:
+        nowValue = PlayerControl.GetMainLevelNowValue(curPlayer)
+        GameWorld.DebugLog("主线boss战斗失败!保持当前刷怪波进度不变! nowValue=%s" % nowValue, playerID)
+        return
+    
+    if turnFight.haveNextLineup():
+        GameWorld.DebugLog("主线boss小队战斗胜利,有下一小队! chapterID=%s,levelNum=%s,lineupIndex=%s,lineupIDList=%s" 
+                           % (chapterID, levelNum, turnFight.lineupIndex, turnFight.lineupIDList), playerID)
+        return
+    
+    isAllPass = False # 是否通关
+    if IpyGameDataPY.GetIpyGameDataNotLog("MainLevel", chapterID, levelNum + 1):
+        nextChapterID, nextLevelNum = chapterID, levelNum + 1
+        GameWorld.DebugLog("主线boss波战斗胜利!下一关! chapterID=%s,levelNum=%s,nextLevelNum=%s" 
+                           % (chapterID, levelNum, nextLevelNum), playerID)
+    elif IpyGameDataPY.GetIpyGameDataNotLog("MainLevel", chapterID + 1, 1):
+        nextChapterID, nextLevelNum = chapterID + 1, 1
+        GameWorld.DebugLog("主线boss波战斗胜利!下一章! chapterID=%s,levelNum=%s,nextChapterID=%s,nextLevelNum=%s" 
+                           % (chapterID, levelNum, nextChapterID, nextLevelNum), playerID)
+    else:
+        # 已通关的暂时先保持不变
+        # 注意防范最后一关奖励重复获得
+        nextChapterID, nextLevelNum = chapterID + 1, 1
+        GameWorld.DebugLog("主线boss波战斗胜利!已通关! chapterID=%s,levelNum=%s,nextChapterID=%s,nextLevelNum=%s" 
+                            % (chapterID, levelNum, nextChapterID, nextLevelNum), playerID)
+        isAllPass = True
+        
+    updPassValue = PlayerControl.SetMainLevelPassInfo(curPlayer, nextChapterID, nextLevelNum, 0)
+    if isAllPass:
+        # 已通关的刷怪进度保持不变
+        updNowValue = PlayerControl.GetMainLevelNowValue(curPlayer)
+        GameWorld.DebugLog("已通关的刷怪进度保持不变: updNowValue=%s" % updNowValue, playerID)
+    else:
+        updNowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, nextChapterID, nextLevelNum, 1)
+        GameWorld.DebugLog("为通关的刷怪进度设置为下一关的第1波: updNowValue=%s" % updNowValue, playerID)
+    GameWorld.DebugLog("updPassValue=%s,updNowValue=%s" % (updPassValue, updNowValue), playerID)
+    
+    # 发放过关奖励
+    levelIpyData = IpyGameDataPY.GetIpyGameData("MainLevel", chapterID, levelNum)
+    if not levelIpyData:
+        return
+    itemList = levelIpyData.GetAwardItemList()
+    GameWorld.DebugLog("过关奖励: chapterID=%s,levelNum=%s,itemList=%s" % (chapterID, levelNum, itemList), playerID)
+    
+    ItemControler.GivePlayerItemOrMail(curPlayer, itemList, event=["MainLevelBoss", False, {}], isNotifyAward=False)
+    
+    overMsg.update({FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(itemList)})
+    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
index 6b6254a..b82cb93 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -3579,7 +3579,6 @@
             #if curPlayer.GetMaxMP() > 0:
             #    curPlayer.SetMP(curPlayer.GetMaxMP())
             
-            FBLogic.OnPlayerLVUp(curPlayer)
             # 记录开服活动冲级数据
             #OpenServerCampaign.UpdOpenServerCampaignRecordData(curPlayer, ShareDefine.Def_Campaign_Type_LV, curPlayer.GetLV())
             #神秘限购
@@ -4417,7 +4416,11 @@
 def GetMainLevelPassInfo(curPlayer):
     ## 获取主线关卡过关进度信息
     # @return: chapterID, levelNum, wave
-    return GetMainLevelValue(GetMainLevelPassValue(curPlayer))
+    chapterID, levelNum, wave = GetMainLevelValue(GetMainLevelPassValue(curPlayer))
+    if not chapterID and not levelNum:
+        chapterID, levelNum, wave = 1, 1, 0
+        SetMainLevelPassValue(curPlayer, ComMainLevelValue(chapterID, levelNum, wave))
+    return chapterID, levelNum, wave
 
 ## 主线关卡当前进度值 = 章节*10000+关卡编号*100+第x波
 def GetMainLevelNowValue(curPlayer): return curPlayer.GetExAttr2()
@@ -4433,7 +4436,14 @@
 def GetMainLevelNowInfo(curPlayer):
     ## 获取主线关卡当前进度信息
     # @return: chapterID, levelNum, wave
-    return GetMainLevelValue(GetMainLevelNowValue(curPlayer))
+    chapterID, levelNum, wave = GetMainLevelValue(GetMainLevelNowValue(curPlayer))
+    if not chapterID and not levelNum:
+        chapterID, levelNum, wave = 1, 1, 1
+        SetMainLevelNowInfo(curPlayer, chapterID, levelNum, wave)
+    if not wave:
+        wave = 1
+        SetMainLevelNowInfo(curPlayer, chapterID, levelNum, wave)
+    return chapterID, levelNum, wave
 
 def ComMainLevelValue(chapterID, levelNum, wave=0): return chapterID * 10000 + levelNum * 100 + wave
 def GetMainLevelValue(value):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py
index bbb2c04..73e7616 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerOnline.py
@@ -772,8 +772,6 @@
             GameWorld.DebugLog("主阵容变化,重新开始战斗", playerID)
             if mainTurnFight.mapID == ChConfig.Def_FBMapID_Main:
                 TurnAttack.__doMainLevelWave(curPlayer, True)
-            elif mainTurnFight.mapID == ChConfig.Def_FBMapID_MainBoss:
-                TurnAttack.__doMainBossStart(curPlayer)
                 
         # 否则只重新设置战斗属性
         else:

--
Gitblit v1.8.0