From e926fc46837c5fb26c537ecb15945a78e2f3423f Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期一, 17 十一月 2025 19:53:58 +0800
Subject: [PATCH] 129 【战斗】战斗系统-服务端(完善战斗相关公式参数;竞技增减伤属性、战力系数改为PVP增减伤;)

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py |  234 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 210 insertions(+), 24 deletions(-)

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 1a0144f..814a105 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
@@ -27,6 +27,8 @@
 import NetPackCommon
 import PlayerControl
 import GameWorld
+import PlayerLLMJ
+import PlayerPrestigeSys
 import IpyGameDataPY
 import PlayerOnline
 import NPCCommon
@@ -45,6 +47,8 @@
 import random
 import time
 import json
+
+g_gmTestFightReq = []
 
 PosNumMax = 7 # 最大站位编号
 ActionNumStart = -1 # 起始行动位置编号,一般是从1开始,如果有加主公、红颜等则扣除相应位置值,如从0或-1开始
@@ -71,6 +75,7 @@
         self.shapeType = 0 # 阵型
         self.fightPower = 0 # 阵容总战力
         self.posObjIDDict = {} # 站位对应战斗实体 {站位编号:batObjID, ...}, 站位编号小于0为非主战单位,如主公、红颜等
+        self.heroObjIDDict = {} # 武将ID对应ObjID {heroID:batObjID, ...}
         self.lingshouObjIDDict = {} # 灵兽战斗单位 {位置编号:batObjID, ...}
         self.beautyObjIDDict = {} # 红颜战斗单位 {位置编号:batObjID, ...}
         self.actionNum = ActionNumStart # 行动位置,从1开始
@@ -107,6 +112,7 @@
         for objID in self.beautyObjIDDict.values():
             batObjMgr.delBatObj(objID)
         self.posObjIDDict = {}
+        self.heroObjIDDict = {}
         self.lingshouObjIDDict = {}
         self.beautyObjIDDict = {}
         self.fightPower = 0
@@ -182,6 +188,7 @@
         self.isNeedReport = isNeedReport # 是否需要战报
         self.msgDict = {} # 扩展信息字典,一般由MapID绑定的功能决定信息内容  {k:v, ...}
         self._kvDict = {} # 自定义信息字典,不会被重置  {k:v, ...}
+        self.awardData = None # 战斗奖励设置的数据,可以是任意数据格式,由功能自行决定,会传递给 OnTurnFightAward 处理
         
         self.factionDict = {} # 战斗阵营 {faction:BatFaction, ...},一般是只有两个阵营,faction为1或2,每个阵营支持多个阵容
         self.actionSortList = [] # 阵容行动顺序 [[faction, num], ...]
@@ -211,6 +218,7 @@
         self.setPVP()
         self.msgDict = {}
         self._kvDict = {}
+        self.awardData = None
         self.nextTurnFight(msgDict)
         return
     
@@ -225,6 +233,8 @@
         self.tagPlayerID = tagPlayerID
         self.tagViewCache = tagViewCache
         return
+    
+    def getPVPPlayerID(self): return self.tagPlayerID # 获取PVP目标玩家ID,也可用于判断是否PVP
     
     def isFBMap(self):
         ## 是否副本地图中,非主线的均视为副本
@@ -512,6 +522,18 @@
         self.waveMax = 6 # 本关最大波数,每波有多个小队,每个小队即为一张战斗 TurnFight
         self.wave = 0 # 当前刷怪波,注意不是玩家当前进度波,比如被击杀会回退一波
         self.turnFight = GetTurnFightMgr().addTurnFight(ChConfig.Def_FBMapID_Main, 0, playerID)
+        
+        # 主线小怪战斗额外数据,一般用于分割与主线战斗表现无关的附加功能内容
+        self.useZhanchui = 0
+        self.mjExp = 0 # 历练秘笈额外经验
+        self.killNPCCnt = 0
+        return
+    
+    def resetMainFightExDataRec(self):
+        ## 重置主线战斗相关的额外数据记录,每次前端请求主线小怪战斗处理后需要重置
+        self.useZhanchui = 0
+        self.mjExp = 0
+        self.killNPCCnt = 0
         return
     
     def isLevelBoss(self):
@@ -605,7 +627,10 @@
     # @param lineupID: 阵容ID
     # @param npcLV: 成长NPC等级
     # @param difficulty: 成长NPC难度系数
-    # @return: 阵容全部信息json字典,前端通用格式
+    # @return: 阵容全部信息json字典,前端通用格式    
+    lineupInfo = GetGMTestNPCLineupInfo(lineupID, strongerLV, difficulty)
+    if lineupInfo:
+        return lineupInfo
     ipyData = IpyGameDataPY.GetIpyGameData("NPCLineup", lineupID)
     if not ipyData:
         return {}
@@ -627,6 +652,93 @@
     lineupInfo = {"NPCLineupID":lineupID, "Hero":heroDict, "BossID":bossID, "BossPosView":bossPosView}
     return lineupInfo
 
+def GMTestFight(curPlayer, heroIDList, isAllSkill):
+    ## GM测试战斗,指定武将
+    global g_gmTestFightReq
+    g_gmTestFightReq = [heroIDList, isAllSkill]
+    __doMainLevelWave(curPlayer, True)
+    g_gmTestFightReq = []
+    return
+
+def GetGMTestNPCLineupInfo(lineupID, strongerLV=0, difficulty=0):
+    ## 获取GM测试战斗阵容信息
+    if not g_gmTestFightReq:
+        return
+    heroIDList, isAllSkill = g_gmTestFightReq
+    
+    lineupIpyData = IpyGameDataPY.GetIpyGameData("NPCLineup", lineupID)
+    if not lineupIpyData:
+        return
+    
+    npcDict = {}
+    heroNPCIDDict = {}
+    for posNum in range(1, 1 + 6):
+        if not hasattr(lineupIpyData, "GetPosNPCID%s" % posNum):
+            break
+        npcID = getattr(lineupIpyData, "GetPosNPCID%s" % posNum)()
+        if not npcID:
+            continue
+        battleDict = GetNPCBattleDict(lineupIpyData, npcID, strongerLV, difficulty)
+        if not battleDict:
+            continue
+        npcDict[npcID] = battleDict
+        heroID = battleDict["HeroID"]
+        heroNPCIDDict[heroID] = npcID
+        
+    if not npcDict:
+        return
+    
+    heroDict = {}
+    # 原先阵容刚好有的直接用
+    for posNum, heroID in enumerate(heroIDList, 1):
+        if heroID not in heroNPCIDDict:
+            continue
+        npcID = heroNPCIDDict[heroID]
+        heroDict[str(posNum)] = npcDict[npcID]
+        heroIDList[posNum - 1] = 0
+        
+    lineupNPCID = npcDict.keys()[0]
+    ipyNPCIDList = []
+    ipyHeroIDList = []
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in xrange(ipyDataMgr.GetNPCCount()):
+        ipyData = ipyDataMgr.GetNPCByIndex(index)
+        heroID = ipyData.GetRelatedHeroID()
+        npcID = ipyData.GetNPCID()
+        ipyNPCIDList.append(npcID)
+        ipyHeroIDList.append(heroID)
+        
+    lineupNPCIndex = ipyNPCIDList.index(lineupNPCID)
+    # 从参考阵容先往前检索,再往后检索,还没有对应武将的NPC
+    loopIndexList = range(lineupNPCIndex + 1)[::-1] + range(lineupNPCIndex + 1, ipyDataMgr.GetNPCCount())
+    for index in loopIndexList:
+        npcID = ipyNPCIDList[index]
+        heroID = ipyHeroIDList[index]
+        if heroID not in heroIDList:
+            continue
+        battleDict = GetNPCBattleDict(lineupIpyData, npcID, strongerLV, difficulty)
+        if not battleDict:
+            continue
+        
+        for posNum, posHeroID in enumerate(heroIDList, 1):
+            if not posHeroID or heroID != posHeroID:
+                continue
+            heroIDList[posNum - 1] = 0
+            heroDict[str(posNum)] = battleDict
+        
+        if heroIDList.count(0) == len(heroIDList):
+            break
+        
+    if isAllSkill:
+        for battleDict in heroDict.values():
+            heroID = battleDict["HeroID"]
+            heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID) if heroID else None
+            skillIDList = GetNPCHeroSkillIDList(heroID, heroIpyData, 99999, 99999) # 默认取突破、觉醒都满级时的技能
+            battleDict["SkillIDList"] = skillIDList
+            
+    lineupInfo = {"NPCLineupID":lineupID, "Hero":heroDict, "BossID":0, "BossPosView":0}
+    return lineupInfo
+    
 def GetNPCBattleDict(lineupIpyData, npcID, strongerLV=0, difficulty=0):
     ## 获取NPC战斗相关字典,支持成长NPC
     # @param strongerLV: 成长等级
@@ -755,7 +867,9 @@
     @param lineupInfo: 阵容信息
     @param playerID: 发起的玩家ID,系统场次为0
     '''
-    GameWorld.DebugLog("SummonLineupObjs faction:%s,num:%s,lineupInfo=%s" % (faction, num, lineupInfo), playerID)
+    lineupPlayerID = lineupInfo.get("PlayerID", 0) # 阵容所属玩家ID
+    npcLineupID = lineupInfo.get("NPCLineupID", 0)
+    GameWorld.DebugLog("SummonLineupObjs faction:%s,num:%s,npcLineupID=%s,lineupPlayerID=%s" % (faction, num, npcLineupID, lineupPlayerID), playerID)
     if playerID:
         curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
         if not curPlayer:
@@ -763,7 +877,6 @@
         
     turnFight = batLineup.turnFight
     tfGUID = turnFight.guid
-    lineupPlayerID = lineupInfo.get("PlayerID", 0) # 阵容所属玩家ID
     heroDict = lineupInfo.get("Hero", {})
     
     batObjMgr = BattleObj.GetBatObjMgr()
@@ -829,6 +942,7 @@
             skillManager.LearnSkillByID(skillID)
             
         batLineup.posObjIDDict[posNum] = objID
+        batLineup.heroObjIDDict[heroID] = objID
         GameWorld.DebugLog("AddBatObj %s,skill=%s" % (GetObjName(batObj), skillManager.GetSkillIDList()))
         ResetObjSkill(batObj)
         
@@ -1011,7 +1125,9 @@
             break
         
     PlayerOnline.GetOnlinePlayer(curPlayer).SetLastBatBuffer(guid, turnFight.batBuffer)
-    SyncTurnFightReport(curPlayer, guid, turnFight.batBuffer)
+    SyncTurnFightReport(curPlayer, guid, turnFight.batBuffer) # 同步战报
+    #在同步战报后再处理结算奖励,与战斗表现分离
+    FBLogic.OnTurnFightAward(curPlayer, turnFight, mapID, funcLineID, turnFight.awardData)
     tfMgr.delTurnFight(guid)
     return True
 
@@ -1050,6 +1166,7 @@
     
     PlayerOnline.GetOnlinePlayer(curPlayer).SetLastBatBuffer(guid, turnFight.batBuffer)
     SyncTurnFightReport(curPlayer, guid, turnFight.batBuffer)
+    FBLogic.OnTurnFightAward(curPlayer, turnFight, mapID, funcLineID, turnFight.awardData)
     tfMgr.delTurnFight(guid)
     return True
 
@@ -1080,6 +1197,9 @@
         return
     
     GameWorld.DebugLog("------------------- 主线战斗请求: reqType=%s" % reqType, curPlayer.GetPlayerID())
+    mainFightMgr = GetMainFightMgr(curPlayer)
+    mainFightMgr.resetMainFightExDataRec() # 请求时补重置,防止异常时重复结算逻辑
+    
     clientPack = ChPyNetSendPack.tagSCTurnFightReportSign()
     clientPack.Sign = 0
     NetPackCommon.SendFakePack(curPlayer, clientPack) # 标记开始
@@ -1094,6 +1214,34 @@
     # 标记结束
     clientPack.Sign = 1
     NetPackCommon.SendFakePack(curPlayer, clientPack)
+    
+    __doMainFightExDataFunc(curPlayer)
+    return
+
+def __doMainFightExDataFunc(curPlayer):
+    mainFightMgr = GetMainFightMgr(curPlayer)
+    
+    # 本次消耗战锤数
+    useZhanchui = mainFightMgr.useZhanchui
+    if useZhanchui > 0:
+        PlayerLLMJ.AddUseZhanchui(curPlayer, useZhanchui)
+        PlayerPrestigeSys.AddRealmTaskValue(curPlayer, PlayerPrestigeSys.RealmTaskType_UseXiantao, useZhanchui)
+        PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_CutTree, useZhanchui)
+        PlayerActivity.AddDailyTaskValue(curPlayer, ChConfig.DailyTask_CutTree, useZhanchui)
+        
+    # 历练秘境额外经验
+    mjExp = mainFightMgr.mjExp
+    if mjExp > 0:
+        PlayerLLMJ.AddExpEx(curPlayer, mjExp)
+        
+    # 击杀怪物
+    killNPCCnt = mainFightMgr.killNPCCnt
+    if killNPCCnt > 0:
+        PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_KillNPC, killNPCCnt)
+        PlayerActivity.AddDailyTaskValue(curPlayer, ChConfig.DailyTask_KillNPC, killNPCCnt)
+        
+    # 结算逻辑最后重置数据
+    mainFightMgr.resetMainFightExDataRec()
     return
 
 def __doExitMainFight(curPlayer):
@@ -1412,7 +1560,7 @@
     
     batObjMgr = BattleObj.GetBatObjMgr()
     for faction, num in turnFight.actionSortList:
-        GameWorld.DebugLog("大回合开始逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
+        GameWorld.DebugLog("大回合开始逻辑: turnNum=%s,faction=%s,num=%s" % (turnNum, faction, num))
         batFaction = turnFight.getBatFaction(faction)
         batLineup = batFaction.getBatlineup(num)
         batLineup.actionNum = 1
@@ -1426,8 +1574,9 @@
             turnFight.ResetOneActionUseSkillCnt()
             batObj.SetTiming(ChConfig.TurnTiming_Before) # 重置时机到回合前
             if turnNum > 1: # 第1回合不用刷新技能
-                RefreshObjSkillByTurn(batObj)
-                
+                RefreshObjSkillByBigTurn(batObj)
+                RefreshObjByBigTurn(turnFight, batObj)
+            batObj.ResetBigTurn() # 每大回合重置
             TurnPassive.OnTriggerPassiveEffect(turnFight, batObj, ChConfig.TriggerWay_BigTurnStart)
             
     return
@@ -1498,9 +1647,11 @@
             GameWorld.DebugLog("技能初始CD: curID=%s,skillID=%s,initCD=%s" % (curID, skillID, initCD))
         elif curSkill.GetRemainTime():
             curSkill.SetRemainTime(0)
+        curSkill.SetEnergy(0)
+        
     return
 
-def RefreshObjSkillByTurn(batObj):
+def RefreshObjSkillByBigTurn(batObj):
     '''按回合刷新技能:默认以大回合统一减1回合
     '''
     curID = batObj.GetID()
@@ -1520,8 +1671,31 @@
         remainTime -= 1
         curSkill.SetRemainTime(remainTime)
         GameWorld.DebugLog("    更新技能CD: curID=%s,skillID=%s,remainTime=%s" % (curID, skillID, remainTime))
-        
-    batObj.ResetSkillTurnUseCnt() # 重置回合使用次数,放刷新CD后重置
+    return
+
+def RefreshObjByBigTurn(turnFight, batObj):
+    ## 根据大回合开始刷新buff持续时间,每个大回合-1,第1回合不处理
+    curID = batObj.GetID()
+    buffMgr = batObj.GetBuffManager()
+    for index in range(buffMgr.GetBuffCount())[::-1]:
+        buff = buffMgr.GetBuffByIndex(index)
+        buffID = buff.GetBuffID()
+        skillID = buff.GetSkillID()
+        skillData = buff.GetSkillData()
+        if skillData.GetLastTimeType() != ChConfig.BuffLastTimeType_BigTurn:
+            continue
+        if skillData.GetSkillType() in ChConfig.Def_LstBuff_List:
+            #GameWorld.DebugLog("    持续类buff由触发时机决定剩余时间! curID=%s,index=%s,skillID=%s,buffID=%s" % (curID, index, skillID, buffID))
+            continue
+        if skillData.GetSkillType() == ChConfig.Def_SkillType_Halo and buff.GetOwnerID() != curID:
+            GameWorld.DebugLog("    光环buff非光源不处理! curID=%s,index=%s,skillID=%s,buffID=%s" % (curID, index, skillID, buffID))
+            continue
+        remainTime = buff.GetRemainTime()
+        if remainTime <= 0:
+            continue
+        remainTime -= 1
+        GameWorld.DebugLog("    更新buff回合: curID=%s,buffID=%s,skillID=%s,remainTime=%s" % (curID, buffID, skillID, remainTime))
+        TurnBuff.SetBuffRemainTime(turnFight, batObj, buff, remainTime)
     return
 
 def RefreshObjBuffTime(turnFight, batObj):
@@ -1536,8 +1710,13 @@
         buffID = buff.GetBuffID()
         skillID = buff.GetSkillID()
         skillData = buff.GetSkillData()
+        if skillData.GetLastTimeType() != ChConfig.BuffLastTimeType_Default:
+            continue
         if skillData.GetSkillType() in ChConfig.Def_LstBuff_List:
             #GameWorld.DebugLog("    持续类buff由触发时机决定剩余时间! curID=%s,index=%s,skillID=%s,buffID=%s" % (curID, index, skillID, buffID))
+            continue
+        if skillData.GetSkillType() == ChConfig.Def_SkillType_Halo and buff.GetOwnerID() != curID:
+            GameWorld.DebugLog("    光环buff非光源不处理! curID=%s,index=%s,skillID=%s,buffID=%s" % (curID, index, skillID, buffID))
             continue
         remainTime = buff.GetRemainTime()
         if remainTime <= 0:
@@ -1554,13 +1733,9 @@
             continue
         remainTime -= 1
         GameWorld.DebugLog("    更新buff回合: curID=%s,buffID=%s,skillID=%s,remainTime=%s,addTiming=%s" % (curID, buffID, skillID, remainTime, addTiming))
-        if remainTime > 0:
-            buff.SetRemainTime(remainTime)
-            TurnBuff.SyncBuffRefresh(turnFight, batObj, buff)
-        else:
-            TurnBuff.DoBuffDel(turnFight, batObj, buff)
+        TurnBuff.SetBuffRemainTime(turnFight, batObj, buff, remainTime)
     return
-                        
+
 def AddTurnObjCureHP(curObj, srcObj, addValue, cureHP, skillID=0):
     ## 回合对象添加治疗值
     # @param curObj: 获得治疗的对象
@@ -1577,22 +1752,21 @@
         
     return
 
-def AddTurnObjHurtValue(curBatObj, tagBatObj, hurtValue, lostHP, skillID=0, isBounce=False):
+def AddTurnObjHurtValue(curBatObj, tagBatObj, hurtValue, lostHP, skillID=0, lostType=""):
     ## 回合对象添加伤害值
-    # @param isBounce: 是否反弹伤害
     if hurtValue <= 0:
         return
     curID = curBatObj.GetID()
     tagID = tagBatObj.GetID()
     if curID != tagID:
         updStatValue = curBatObj.StatHurtValue(hurtValue)
-        GameWorld.DebugLog("        统计伤血: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,tagHP=%s,isBounce=%s" 
-                       % (curID, tagID, skillID, hurtValue, lostHP, updStatValue, tagBatObj.GetHP(), isBounce))
+        GameWorld.DebugLog("        统计输出: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,tagHP=%s,lostType=%s" 
+                       % (curID, tagID, skillID, hurtValue, lostHP, updStatValue, tagBatObj.GetHP(), lostType))
         
         if tagBatObj:
             updStatValue = tagBatObj.StatDefValue(hurtValue)
-            GameWorld.DebugLog("        统计承伤: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,curHP=%s,isBounce=%s" 
-                           % (tagID, curID, skillID, hurtValue, lostHP, updStatValue, tagBatObj.GetHP(), isBounce))
+            GameWorld.DebugLog("        统计承伤: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,updStatValue=%s,curHP=%s,lostType=%s" 
+                           % (tagID, curID, skillID, hurtValue, lostHP, updStatValue, tagBatObj.GetHP(), lostType))
             
     else:
         # 如换血类技能,自残的伤害不算输出
@@ -1695,8 +1869,20 @@
     
     # 暂时只算主线小怪
     if curPlayer and turnFight.mapID == ChConfig.Def_FBMapID_Main and gameObj.GetFaction() != ChConfig.Def_FactionA:
-        PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_KillNPC, 1)
-        PlayerActivity.AddDailyTaskValue(curPlayer, ChConfig.DailyTask_KillNPC, 1)
+        GetMainFightMgr(curPlayer).killNPCCnt += 1
+        
+    # 清除光源buff
+    buffMgr = gameObj.GetBuffManager()
+    for index in range(buffMgr.GetBuffCount())[::-1]:
+        buff = buffMgr.GetBuffByIndex(index)
+        skillID = buff.GetSkillID()
+        skillData = buff.GetSkillData()
+        if skillData.GetSkillType() != ChConfig.Def_SkillType_Halo:
+            continue
+        if buff.GetOwnerID() != objID:
+            continue
+        GameWorld.DebugLog("删除光环buff: objID=%s,skillID=%s" % (objID, skillID))
+        TurnBuff.DoBuffDel(turnFight, gameObj, buff)
     return True
 
 def OnTurnAllOver(guid):

--
Gitblit v1.8.0