From 05251cfca26ce5f044923793da7c97597413ed1b Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期三, 13 八月 2025 19:18:54 +0800
Subject: [PATCH] 129 【战斗】战斗系统-服务端(主线掉落物品默认不堆叠;注:单个物品的个数可能大于1,单个战利品ID可能一次掉多个;)
---
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py | 1686 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 1,555 insertions(+), 131 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 3e17b07..4e600cb 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
@@ -10,6 +10,10 @@
# @version 1.0
#
# 详细描述: 回合制攻击逻辑,均使用NPC实例作为战斗主体
+# 实时战斗: 仅用于主线,每个玩家最多仅允许存在一个实时战斗
+# 由前端控制战斗流程,后端进行计算
+# 瞬时战斗: 用于非主线外的各种战斗,如副本PVE挑战,PVP挑战等,一样仅有一个瞬时战斗,
+# 一次性处理完,后端控制整体战斗流程及计算,前端仅做战报解析表现战斗过程
#
#-------------------------------------------------------------------------------
#"""Version = 2023-11-30 15:30"""
@@ -22,7 +26,25 @@
import GameWorld
import GameObj
import FBCommon
-import FBLogic
+import IpyGameDataPY
+import PlayerOnline
+import NPCCommon
+import ShareDefine
+import PyGameData
+import ItemControler
+import SkillCommon
+import AttackCommon
+import BattleObj
+import TurnSkill
+import TurnBuff
+import ObjPool
+
+import random
+import time
+import json
+
+PosNumMax = 10 # 最大站位编号
+ActionNumStart = -1 # 起始行动位置编号,一般是从1开始,如果有加主公、红颜等则扣除相应位置值,如从0或-1开始
# 回合战斗流程状态
(
@@ -33,170 +55,1572 @@
FightState_Award, # 4 结算奖励
FightState_Over, # 5 结束状态,无特殊意义,仅代表所有处理结束了,与Start对应
) = range(6)
+
+class BatLineup():
+ ## 战斗阵容
+
+ def __init__(self, faction, num, turnFight):
+ self.turnFight = turnFight # TurnFight
+ self.faction = faction # 所属阵营
+ self.num = num # 该阵容所在阵营中的编号,不同阵营可重复,多V多
+ self.ownerID = 0 # 阵容所属玩家ID,可能为0,0代表非玩家阵容
+ self.shapeType = 0 # 阵型
+ self.fightPower = 0 # 阵容总战力
+ self.posObjIDDict = {} # 站位对应战斗实体 {站位编号:batObjID, ...}, 站位编号小于0为非主战单位,如主公、红颜等
+ self.actionNum = ActionNumStart # 行动位置,从1开始
+ return
+
+ def getPlayerID(self): return self.turnFight.playerID # 发起的玩家ID
+
+ def isEmpty(self): return not self.posObjIDDict
+
+ def setLineup(self, lineupInfo):
+ ## 设置阵容
+ # @param lineupInfo: 阵容信息
+ self.clearLineup()
+ self.ownerID = lineupInfo.get("PlayerID", 0) # 阵容所属的玩家ID
+ self.shapeType = lineupInfo.get("ShapeType", 0)
+ self.fightPower = lineupInfo.get("FightPower", 0)
+ SummonLineupObjs(self, self.faction, self.num, lineupInfo, self.getPlayerID())
+ return
+
+ def clearLineup(self):
+ ## 清除阵容
+ if not self.posObjIDDict:
+ return
+ batObjMgr = BattleObj.GetBatObjMgr()
+ for objID in self.posObjIDDict.values():
+ batObjMgr.delBatObj(objID)
+ self.posObjIDDict = {}
+ self.fightPower = 0
+ return
+
+class BatFaction():
+ ## 战斗阵营
+
+ def __init__(self, faction, turnFight):
+ self.turnFight = turnFight # TurnFight
+ self.faction = faction
+ self.lineupDict = {} # 该阵营所有阵容信息 {编号:BatLineup, ...}
+ return
+
+ def getBatlineup(self, num=1):
+ ## 获取战斗阵容
+ lineup = None
+ if num in self.lineupDict:
+ lineup = self.lineupDict[num]
+ else:
+ lineup = BatLineup(self.faction, num, self.turnFight)
+ self.lineupDict[num] = lineup
+ return lineup
+
+ def clearLineups(self):
+ ## 清除所有战斗阵容
+ for lineup in self.lineupDict.values():
+ lineup.clearLineup()
+ return
+
+class TurnFight():
+ '''某场回合战斗,支持多V多,所有战斗通用,主线、爬塔、PVP等
+ 可能为系统后台发起的,则攻击方、防守方阵容以玩家所保存的阵容镜像进行战斗
+ 如果玩家发起的,则攻击方为发起方玩家,目标为防守方,支持多V多,如组队PK
+ 多V多的情况也是某个队员各自发起战斗,只是用到队友的镜像阵容一起战斗,队友无感知
+
+ 多V多可使用两种模式:
+ 1. 多个阵容一起出场,在一场战斗中完成
+ 2. 以单阵容方式轮流出场战斗,该方式同样可以使用方式1的逻辑实现,固优先使用方式1战斗逻辑,方便扩展
+ '''
+
+ def __init__(self, mapID=0, funcLineID=0, playerID=0, isNeedReport=False):
+ self.guid = GameWorld.GetGUID() # 某场战斗的唯一guid,可用于存储记录如战报等
+ self.playerID = playerID # 可能为0,系统后台自动处理的战斗时为0,某个玩家发起则为发起玩家ID,同个玩家ID可能同时存在多场战斗,如主线+其他
+ self.curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID) if playerID else None
+ self.mapID = mapID
+ self.funcLineID = funcLineID
+ self.state = -1 # -1 代表未战斗
+ self.turnNum = 1 # 当前第x回合,默认第1回合开始
+ self.turnMax = 15 # 最大回合数
+ self.enterLogic = False # 是否已执行进场逻辑
+ self.turnStart = 0 # 已执行回合开始值,如第1回合开始已执行则为1,第2回合为2
+ self.turnEnd = 0 # 已执行回合结束值,如第1回合结束已执行则为1,第2回合为2
+ self.winFaction = 0 # 本场战斗结束标记,获胜阵营,为0时代表未结束,所有小队打完或失败才有结果,0-未结束,>0-获胜的阵营
+ self.batBuffer = "" # 战报buffer,战报暂时只保留最后一个小队的
+ self.isNeedReport = isNeedReport # 是否需要战报
+ self.msgDict = {} # 扩展信息字典,一般由MapID绑定的功能决定信息内容 {k:v, ...}
+
+ self.factionDict = {} # 战斗阵营 {faction:BatFaction, ...},一般是只有两个阵营,faction为1或2,每个阵营支持多个阵容
+ self.actionSortList = [] # 阵容行动顺序 [[faction, num], ...]
+ self.actionIndex = 0 # 行动顺序索引
+
+ self.startTime = 0 # 开始时间戳,支持毫秒小数
+ self.costTime = 0 # 单场战斗总耗时,支持毫秒小数
+ return
+
+ def setTurn(self, mapID, funcLineID, turnMax, isNeedReport=False, msgDict={}):
+ ## 设置本场回合战斗设定
+ self.mapID = mapID
+ self.funcLineID = funcLineID
+ self.turnMax = turnMax # 最大回合数
+ self.isNeedReport = isNeedReport
+ self.msgDict = {}
+ self.resetTurn(msgDict)
+ return
+
+ def resetTurn(self, msgDict):
+ ## 一般用于玩家发起的战斗,在需要保留玩家阵容属性及状态的情况下,重置回合进入下一场战斗
+ self.turnNum = 1
+ self.enterLogic = False
+ self.turnStart = 0
+ self.turnEnd = 0
+ self.winFaction = 0
+ self.batBuffer = "" # 战报buffer
+ self.msgDict.update(msgDict)
+ self.startTime = time.time()
+ self.costTime = 0
+ return
+
+ def setFactionLineup(self, faction, lineupDict):
+ ## 设置阵营阵容
+ # @param lineupDict: {阵容编号:阵容信息, ...} 每个阵营支持多个阵容,即支持多V多
+ batFaction = self.getBatFaction(faction)
+ batFaction.clearLineups()
+ for num, lineupInfo in lineupDict.items():
+ if not lineupInfo:
+ continue
+ batLineup = batFaction.getBatlineup(num)
+ batLineup.setLineup(lineupInfo)
+ return
+
+ def sortActionQueue(self):
+ ## 刷新出手顺序队列
+ sortList = []
+ for batFaction in self.factionDict.values():
+ faction = batFaction.faction
+ for num, batLineup in batFaction.lineupDict.items():
+ fightPower = batLineup.fightPower
+ sortValue = -(faction * 10 + num)
+ sortList.append([fightPower, sortValue, faction, num])
+ sortList.sort(reverse=True) # 战力高的先手
+
+ self.actionIndex = 0
+ self.actionSortList = []
+ for _, _, faction, num in sortList:
+ self.actionSortList.append([faction, num])
+
+ GameWorld.DebugLog("阵容战力排序[fp, sortV, f, n]: %s" % sortList)
+ GameWorld.DebugLog("阵容行动顺序[f, n]: %s" % self.actionSortList)
+ return
+
+ def getBatFaction(self, faction=ChConfig.Def_FactionA):
+ ## 默认阵营1
+ batFaction = None
+ if faction in self.factionDict:
+ batFaction = self.factionDict[faction]
+ else:
+ batFaction = BatFaction(faction, self)
+ self.factionDict[faction] = batFaction
+ return batFaction
+
+ def checkOverByKilled(self):
+ ##检查是否击杀结束
+ # @return: 0-无获胜阵营,同时也代表战斗未结束;>0-获胜的阵营ID,同时也代表战斗已全部结束
+
+ if self.winFaction:
+ return self.winFaction
+
+ batObjMgr = BattleObj.GetBatObjMgr()
+ for faction, batFaction in self.factionDict.items():
+ allKilled = True
+ for batLineup in batFaction.lineupDict.values():
+ if not allKilled:
+ break
+ for posNum, objID in batLineup.posObjIDDict.items():
+ if posNum <= 0:
+ # 非主战位置不判断
+ continue
+ batObj = batObjMgr.getBatObj(objID)
+ if not batObj:
+ continue
+ if batObj.GetHP() > 0:
+ allKilled = False
+ break
+
+ if allKilled:
+ self.winFaction = ChConfig.Def_FactionA if faction == ChConfig.Def_FactionB else ChConfig.Def_FactionA
+ DoTurnFightOver(self.guid)
+ return self.winFaction
+
+ return 0
+
+ def exitFight(self):
+ ## 退出战斗
+ self.syncState(FightState_Over)
+ for batFaction in self.factionDict.values():
+ batFaction.clearLineups()
+ self.state = -1
+ return
+
+ def startFight(self):
+ ## 准备就绪,开始战斗
+ self.state = FightState_Start
+ self.syncInit()
+ return
+
+ def isInFight(self): return self.state != -1
+
+ def syncInit(self):
+ ## 初始化通知
+ msg = json.dumps(self.msgDict, ensure_ascii=False)
+ msg = msg.replace(" ", "")
+ clientPack = ChPyNetSendPack.tagSCTurnFightInit()
+ clientPack.MapID = self.mapID
+ clientPack.FuncLineID = self.funcLineID
+ clientPack.TurnMax = self.turnMax
+ clientPack.Msg = msg
+ clientPack.Len = len(clientPack.Msg)
+ clientPack.FactionList = []
+
+ batObjMgr = BattleObj.GetBatObjMgr()
+ for faction in self.factionDict.keys():
+ batFaction = self.getBatFaction(faction)
+ tfFaction = ChPyNetSendPack.tagSCTurnFightFaction()
+ tfFaction.Faction = faction
+ tfFaction.LineupList = []
+ for num in batFaction.lineupDict.keys():
+ batLineup = batFaction.getBatlineup(num)
+ tfLineup = ChPyNetSendPack.tagSCTurnFightLineup()
+ tfLineup.Num = num
+ tfLineup.OwnerID = batLineup.ownerID
+ tfLineup.ShapeType = batLineup.shapeType
+ tfLineup.ObjList = []
+ for posNum, objID in batLineup.posObjIDDict.items():
+ batObj = batObjMgr.getBatObj(objID)
+ if not batObj:
+ continue
+ tfObj = ChPyNetSendPack.tagSCTurnFightObj()
+ tfObj.ObjID = batObj.GetID()
+ tfObj.NPCID = batObj.GetNPCID()
+ tfObj.HeroID = batObj.GetHeroID()
+ tfObj.SkinID = batObj.GetSkinID()
+ tfObj.HP = batObj.GetHP() % ChConfig.Def_PerPointValue
+ tfObj.HPEx = batObj.GetHP() / ChConfig.Def_PerPointValue
+ tfObj.MaxHP = batObj.GetMaxHP() % ChConfig.Def_PerPointValue
+ tfObj.MaxHPEx = batObj.GetMaxHP() / ChConfig.Def_PerPointValue
+ tfObj.LV = batObj.GetLV()
+ tfObj.PosNum = posNum
+ tfObj.AngreXP = batObj.GetXP()
+ tfLineup.ObjList.append(tfObj)
+ tfLineup.ObjCnt = len(tfLineup.ObjList)
+ tfFaction.LineupList.append(tfLineup)
+ tfFaction.LineupCnt = len(tfFaction.LineupList)
+ clientPack.FactionList.append(tfFaction)
+ clientPack.FactionCnt = len(clientPack.FactionList)
+ self.addBatPack(clientPack)
+ return
+
+ def syncState(self, state, msgDict={}):
+ self.state = state
+ msg = json.dumps(msgDict, ensure_ascii=False)
+ msg = msg.replace(" ", "")
+ clientPack = ChPyNetSendPack.tagMCTurnFightState()
+ clientPack.Clear()
+ clientPack.MapID = self.mapID
+ clientPack.FuncLineID = self.funcLineID
+ clientPack.State = state
+ clientPack.TurnNum = self.turnNum
+ clientPack.Msg = msg
+ clientPack.Len = len(clientPack.Msg)
+ self.addBatPack(clientPack)
+ return
+
+ def syncObjAction(self, turnNum, objID):
+ clientPack = ChPyNetSendPack.tagMCTurnFightObjAction()
+ clientPack.Clear()
+ clientPack.TurnNum = turnNum
+ clientPack.ObjID = objID
+ self.addBatPack(clientPack)
+ return
+
+ def addBatPack(self, clientPack):
+ ## 添加战斗过程封包,非战斗相关的封包可以不使用该函数发送,如加经验、掉落等
+ ## 注:战斗相关的封包需调用本函数方便统一管理战报
+ if hasattr(clientPack, "Head"):
+ 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:
+ ObjPool.GetPoolMgr().release(clientPack)
+ return
+
+class TurnFightMgr():
+ ## 回合战斗管理器
+
+ def __init__(self):
+ self.turnFightDict = {} # {guid:TurnFight, ...}
+ return
+
+ def addTurnFight(self, mapID, funcLineID=0, playerID=0):
+ tf = TurnFight(mapID, funcLineID, playerID, False)
+ self.turnFightDict[tf.guid] = tf
+ return tf
+
+ def delTurnFight(self, guid):
+ turnFight = self.getTurnFight(guid)
+ if not turnFight:
+ return
+ turnFight.exitFight()
+ self.turnFightDict.pop(guid, None)
+ return
+
+ def getTurnFight(self, guid):
+ tf = None
+ if guid in self.turnFightDict:
+ tf = self.turnFightDict[guid]
+ elif False:
+ tf = TurnFight()
+ return tf
+
+def GetTurnFightMgr():
+ tfMgr = None
+ if PyGameData.g_turnFightMgr:
+ tfMgr = PyGameData.g_turnFightMgr
+ else:
+ tfMgr = TurnFightMgr()
+ PyGameData.g_turnFightMgr = tfMgr
+ return tfMgr
+
+class MainFight():
+ ## 主线战斗管理
+
+ def __init__(self, playerID):
+ self.playerID = playerID
+ self.chapterID = 0 # 章节ID
+ self.levelNum = 0 # 关卡编号
+ self.waveMax = 6 # 本关最大波数,每波有多个小队,每个小队即为一张战斗 TurnFight
+ self.wave = 0 # 当前刷怪波,注意不是玩家当前进度波,比如被击杀会回退一波
+ self.teamNum = 1 # 当前小队
+ self.teamMax = 1 # 当前波最大小队,某场战斗可能包含多个小队,所有小队混流击杀完才算过了本波
+ self.nextTeam = False # 下次前端请求战斗是否是下一小队,否则都是重新刷新当前进度怪
+ self.waveLineupList = [] # 小队列表
+ self.turnFight = GetTurnFightMgr().addTurnFight(ChConfig.Def_FBMapID_Main, 0, playerID)
+ return
+
+ def isLevelBoss(self):
+ ## 当前战斗是否关卡boss
+ return self.turnFight.mapID == ChConfig.Def_FBMapID_MainBoss
+
+def GetMainFightMgr(curPlayer):
+ ## 获取主线战斗管理
+ olPlayer = PlayerOnline.GetOnlineMgr().GetOnlinePlayer(curPlayer)
+ return olPlayer.mainFight
-#// B4 10 回合制战斗 #tagCMTurnFight
+def OnPlayerLogin(curPlayer):
+ chapterID, levelNum, _ = PlayerControl.GetMainLevelPassInfo(curPlayer)
+ nowChapterID, _, _ = PlayerControl.GetMainLevelNowInfo(curPlayer)
+ if chapterID != nowChapterID:
+ if IpyGameDataPY.GetIpyGameDataNotLog("MainChapter", chapterID) and IpyGameDataPY.GetIpyGameDataNotLog("MainLevel", chapterID, levelNum):
+ fixNowValue = PlayerControl.SetMainLevelNowInfo(curPlayer, chapterID, levelNum, 1)
+ GameWorld.Log("当前主线关卡章节与过关章节不一致时,且过关进度章节已经存在,强制修正当前刷怪进度为过关章节进度! passChapterID-LV=%s-%s,nowChapterID=%s,fixNowValue=%s"
+ % (chapterID, levelNum, nowChapterID, fixNowValue), curPlayer.GetPlayerID())
+ return
+
+def GetPlayerLineupInfoByCache(playerID, lineupID):
+ ## 获取玩家阵容信息 - 根据玩家查看缓存
+ lineupInfo = {}
+ return lineupInfo
+
+def GetPlayerLineupInfo(curPlayer, lineupID):
+ ## 获取玩家阵容信息,可用于战斗或查看缓存,因为可能取玩家的缓存进行对战,所以统一使用json格式,前端通用
+ # @param lineupID: 阵容ID
+ # @return: 阵容全部信息json字典,前端通用格式
+
+ playerID = curPlayer.GetPlayerID()
+ lineup = PlayerOnline.GetOnlinePlayer(curPlayer).GetLineup(lineupID)
+
+ lineupInfo = {"PlayerID":playerID, "FightPower":lineup.fightPower, "ShapeType":lineup.shapeType}
+ heroDict = {}
+ curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero)
+ for posNum in lineup.lineupHeroDict.keys():
+ hero = lineup.GetLineupHero(posNum)
+ heroID = hero.heroID
+ itemIndex = hero.itemIndex
+ userData = ""
+ heroLV = 1
+ if itemIndex >= 0 and itemIndex < curPack.GetCount():
+ heroItem = curPack.GetAt(itemIndex)
+ if heroItem and not heroItem.IsEmpty():
+ userData = heroItem.GetUserData()
+ heroLV = heroItem.GetUserAttr(ShareDefine.Def_IudetHeroLV)
+
+ skillIDlist = []
+ skillIDlist += hero.heroSkillIDList
+ heroDict[str(posNum)] = {
+ "HeroID":heroID,
+ "SkinID":hero.skinID,
+ "LV":heroLV,
+ "Data":userData,
+ "FightPower":hero.fightPower,
+ "AttrDict":{str(k):v for k, v in hero.heroBatAttrDict.items() if v > 0},
+ "SkillIDList":skillIDlist,
+ }
+ lineupInfo.update({"Hero":heroDict})
+
+ return lineupInfo
+
+def GetNPCLineupInfo(lineupID):
+ ## 获取NPC阵容信息
+ # @param lineupID: 阵容ID
+ # @return: 阵容全部信息json字典,前端通用格式
+ ipyData = IpyGameDataPY.GetIpyGameData("NPCLineup", lineupID)
+ if not ipyData:
+ return {}
+
+ heroDict = {}
+ for posNum in range(1, 1 + 10):
+ if not hasattr(ipyData, "GetPosNPCID%s" % posNum):
+ break
+ npcID = getattr(ipyData, "GetPosNPCID%s" % posNum)()
+ if not npcID:
+ continue
+ npcData = NPCCommon.GetNPCDataPy(npcID)
+ if not npcData:
+ continue
+ batAttrDict = {ChConfig.AttrID_Atk:npcData.GetAtk(), ChConfig.AttrID_Def:npcData.GetDef(), ChConfig.AttrID_MaxHP:npcData.GetMaxHP(),
+ ChConfig.AttrID_FinalDamPer:npcData.GetFinalDamPer(), ChConfig.AttrID_FinalDamPerDef:npcData.GetFinalDamPerDef(),
+ ChConfig.AttrID_MissRate:npcData.GetMissRate(), ChConfig.AttrID_MissRateDef:npcData.GetMissRateDef(),
+ ChConfig.AttrID_SuperHitRate:npcData.GetSuperHitRate(), ChConfig.AttrID_SuperHitRateDef:npcData.GetSuperHitRateDef(),
+ ChConfig.AttrID_StunRate:npcData.GetStunRate(), ChConfig.AttrID_StunRateDef:npcData.GetStunRateDef(),
+ ChConfig.AttrID_ComboRate:npcData.GetComboRate(), ChConfig.AttrID_ComboRateDef:npcData.GetComboRateDef(),
+ ChConfig.AttrID_ParryRate:npcData.GetParryRate(), ChConfig.AttrID_ParryRateDef:npcData.GetParryRateDef(),
+ ChConfig.AttrID_SuckHPPer:npcData.GetSuckHPPer(), ChConfig.AttrID_SuckHPPerDef:npcData.GetSuckHPPerDef(),
+ }
+ batAttrDict.update(npcData.GetSpecAttrInfo())
+ skillIDList = [] + npcData.GetSkillIDList()
+ heroDict[str(posNum)] = {"NPCID":npcID,
+ "AttrDict":{str(k):v for k, v in batAttrDict.items() if v > 0},
+ "SkillIDList":skillIDList
+ }
+
+ lineupInfo = {"NPCLineupID":lineupID, "Hero":heroDict}
+ return lineupInfo
+
+def SummonLineupObjs(batLineup, faction, num, lineupInfo, playerID=0):
+ '''召唤阵容战斗实例
+ @param faction: 所属阵营,目前支持两个阵营,1或2
+ @param num: 战斗阵容在该阵营上的编号,1V1时默认1,多对多时1~n
+ @param lineupInfo: 阵容信息
+ @param playerID: 发起的玩家ID,系统场次为0
+ '''
+ GameWorld.DebugLog("SummonLineupObjs faction:%s,num:%s,lineupInfo=%s" % (faction, num, lineupInfo), playerID)
+ if playerID:
+ curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
+ if not curPlayer:
+ return
+
+ tfGUID = batLineup.turnFight.guid
+ lineupPlayerID = lineupInfo.get("PlayerID", 0) # 阵容所属玩家ID
+ heroDict = lineupInfo.get("Hero", {})
+
+ batObjMgr = BattleObj.GetBatObjMgr()
+ initXP = IpyGameDataPY.GetFuncCfg("AngerXP", 1)
+ atkBackSkillIDList = IpyGameDataPY.GetFuncEvalCfg("ParryCfg", 3)
+ for posNumKey, heroInfo in heroDict.items():
+ posNum = int(posNumKey)
+
+ npcID, heroID, skinID = 0, 0, 0
+ atkBackSkillID = 0 # 反击技能ID
+ fightPower = 0
+ skillIDList = [] # 战斗对象可能改变属性或技能,重新创建,防止误修改来源值
+ attrDict = {}
+ skillIDList += heroInfo.get("SkillIDList", [])
+ attrDict.update(heroInfo.get("AttrDict", {}))
+ objName = ""
+ if lineupPlayerID:
+ heroID = heroInfo.get("HeroID", 0)
+ skinID = heroInfo.get("SkinID", 0)
+ lv = heroInfo.get("LV", 1)
+ fightPower = heroInfo.get("FightPower", 0)
+ heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID)
+ if not heroIpyData:
+ continue
+ atkDistType = heroIpyData.GetAtkDistType()
+ objName = heroIpyData.GetName()
+ else:
+ npcID = heroInfo.get("NPCID", 0)
+ npcDataEx = NPCCommon.GetNPCDataPy(npcID)
+ if not npcDataEx:
+ continue
+ objName = npcDataEx.GetNPCName()
+ atkDistType = npcDataEx.GetAtkDistType()
+ lv = npcDataEx.GetLV()
+
+ batObj = batObjMgr.addBatObj()
+ if not batObj:
+ break
+ objID = batObj.GetID()
+ batObj.SetTFGUID(tfGUID)
+ batObj.SetName(objName)
+ batObj.SetFaction(faction)
+ batObj.SetLineupPos(posNum, num)
+ batObj.SetFightPower(fightPower)
+ batObj.SetLV(lv)
+ if npcID:
+ batObj.SetNPCID(npcID)
+ elif lineupPlayerID:
+ batObj.SetOwnerHero(lineupPlayerID, heroID, skinID)
+
+ if atkDistType == ChConfig.AtkDistType_Short:
+ atkBackSkillID = atkBackSkillIDList[0] if len(atkBackSkillIDList) > 0 else 0
+ elif atkDistType == ChConfig.AtkDistType_Long:
+ atkBackSkillID = atkBackSkillIDList[1] if len(atkBackSkillIDList) > 1 else 0
+ if atkBackSkillID:
+ skillIDList.append(atkBackSkillID)
+ skillManager = batObj.GetSkillManager()
+ skillManager.SkillReset()
+ for skillID in skillIDList:
+ skillManager.LearnSkillByID(skillID)
+
+ batLineup.posObjIDDict[posNum] = objID
+ GameWorld.DebugLog("AddBatObj ID:%s,faction:%s,num=%s,posNum=%s,skill=%s" % (objID, faction, num, posNum, skillIDList))
+ batObj.InitBatAttr({int(k):v for k, v in attrDict.items()}, initXP)
+
+ return
+
+#// B4 13 主线战斗请求 #tagCSMainFightReq
#
-#struct tagCMTurnFight
+#struct tagCSMainFightReq
#{
# tagHead Head;
-# DWORD MapID; // 自定义地图ID,可用于绑定战斗场景功能(如野外关卡,爬塔功能,竞技场等)
-# WORD FuncLineID;
-# BYTE TagType; // 战斗目标类型,0-NPC,1-玩家,2-队伍
-# DWORD TagID; // 战斗目标类型对应的ID
-# BYTE ValueCount;
-# DWORD ValueList[ValueCount]; // 附加值列表,可选,具体含义由MapID决定
+# BYTE ReqType; // 0-停止战斗回城;1-设置消耗倍值;2-挑战关卡小怪;3-挑战关卡boss;4-继续战斗;
+# DWORD ReqValue; // 请求值,ReqType为1时发送消耗倍值
#};
-def OnTurnFight(index, clientData, tick):
+def OnMainFightReq(index, clientData, tick):
curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
- mapID = clientData.MapID
- funcLineID = clientData.FuncLineID
- tagType = clientData.TagType
- tagID = clientData.TagID
- valueList = clientData.ValueList
+ reqType = clientData.ReqType
+ reqValue = clientData.ReqValue
+ if reqType == 0:
+ __doExitMainFight(curPlayer)
+ return
+ elif reqType == 1:
+ __doSetFightPoint(curPlayer, reqValue)
+ return
+
+ clientPack = ChPyNetSendPack.tagSCTurnFightReportSign()
+ clientPack.Sign = 0
+ NetPackCommon.SendFakePack(curPlayer, clientPack) # 标记开始
+
+ if reqType == 2: # 前端主动请求开始关卡小怪的视为从休息中开始
+ __doMainLevelWave(curPlayer, True)
+ elif reqType == 3:
+ __doMainBossStart(curPlayer)
+ elif reqType == 4:
+ __doMainFight(curPlayer, tick)
+ else:
+ pass
+
+ # 标记结束
+ clientPack.Sign = 1
+ NetPackCommon.SendFakePack(curPlayer, clientPack)
+ return
+
+def __doExitMainFight(curPlayer):
+ ## 主线退出战斗 - 回城休息
+ mainFightMgr = GetMainFightMgr(curPlayer)
+ turnFight = mainFightMgr.turnFight
+ if turnFight:
+ turnFight.exitFight()
+ return
+
+def __doSetFightPoint(curPlayer, fightPoint):
+ ## 设置消耗倍值
+ GameWorld.DebugLog("设置战锤消耗倍值: %s" % fightPoint)
+ if fightPoint == 1:
+ pass
+ elif fightPoint == 2:
+ # 条件验证
+ pass
+ elif fightPoint == 3:
+ # 条件验证
+ pass
+ else:
+ return
+ curPlayer.SetFightPoint(fightPoint)
+ return
+
+def __doMainLevelWave(curPlayer, isRestStart=False):
+ ## 开始新的关卡波
+ # @param isRestStart: 是否从休息状态重新开始的
playerID = curPlayer.GetPlayerID()
- if tagType == ChConfig.TurnBattle_TagType_Player:
- if tagID == playerID:
- GameWorld.DebugLog("不能打自己!", playerID)
- return
+ 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):
+ GameWorld.DebugLog("战锤不足,无法开始!")
+ return
+ chapterIpyData = IpyGameDataPY.GetIpyGameData("MainChapter", chapterID)
+ if not chapterIpyData:
+ return
+ levelIpyData = IpyGameDataPY.GetIpyGameData("MainLevel", chapterID, levelNum)
+ if not levelIpyData:
+ # 如上线后减少某个章节关卡数的情况,导致找不到,则从该章节回退到存在的关卡开始
+ while levelNum > 1:
+ levelNum -= 1
+ levelIpyData = IpyGameDataPY.GetIpyGameDataNotLog("MainLevel", chapterID, levelNum)
+ if levelIpyData:
+ break
+ if not levelIpyData:
+ return
+
+ # 本关卡最大波数,暂时支持最大6波
+ waveMax = 6
+ while waveMax >= 1 and (not hasattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax) or not getattr(levelIpyData, "GetWaveLineupIDList%s" % waveMax)()):
+ waveMax -= 1
+ if waveMax < 1:
+ return
+ if wave > waveMax:
+ wave = waveMax
- reqRet = FBLogic.OnTurnFightRequest(curPlayer, mapID, funcLineID, tagType, tagID, valueList)
- if not reqRet:
+ waveLineupList = getattr(levelIpyData, "GetWaveLineupIDList%s" % wave)() # 小队1阵容ID|小队2阵容ID|...
+ if not waveLineupList:
+ return
+ teamMax = len(waveLineupList)
+ teamNum = 1
+ lineupID = waveLineupList[teamNum - 1] # NPC阵容ID
+
+ lineupMainInfo = GetPlayerLineupInfo(curPlayer, ShareDefine.Lineup_Main)
+ if not lineupMainInfo:
+ GameWorld.DebugLog("没有设置主阵容!", playerID)
return
- # 需要发送到GameServer验证
- if mapID in ChConfig.Def_TFMapID_SendToGameServer:
- SendToGameServer_TurnFight(curPlayer, "TurnFightRequest", [mapID, funcLineID, tagType, tagID, valueList])
- return
+ 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
+ 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"
+ % (chapterID, levelNum, wave, waveMax, teamMax, mapID, funcLineID, lineupID), playerID)
- DoTurnFightProcess(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick)
+ 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)})
+ turnFight.sortActionQueue()
+ turnFight.startFight()
return
-def SendToGameServer_TurnFight(curPlayer, msgType, dataMsg=""):
+def __doMainBossStart(curPlayer):
+ ## 开始挑战关卡boss
playerID = curPlayer.GetPlayerID()
- msgList = str([msgType, dataMsg])
- GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(playerID, 0, 0, "TurnFight", msgList, len(msgList))
- GameWorld.Log("回合战斗发送GameServer: %s, %s" % (msgType, dataMsg), playerID)
- return
-
-def GameServer_TurnFight_DoResult(curPlayer, msgData, tick):
-
- msgType, dataMsg, ret = msgData
-
- if not ret:
+ 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
- if msgType == "TurnFightRequest":
- mapID, funcLineID, tagType, tagID, valueList = dataMsg
- DoTurnFightProcess(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick)
+ 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
- elif msgType == "TurnFightOver":
- mapID, funcLineID, tagType, tagID, valueList, fightRet, awardItemList = dataMsg
- FBLogic.OnTurnFightOver_GameServerRet(curPlayer, mapID, funcLineID, tagType, tagID, valueList, fightRet, awardItemList, ret)
-
- elif msgType == "TurnFightTagPlayerInfo":
- mapID, funcLineID, tagType, tagID, valueList = dataMsg
- DoTrunFightVSPlayer(curPlayer, tagID, [mapID, funcLineID, valueList], ret)
-
+ 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
+
+ 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
+ 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"
+ % (chapterID, levelNum, wave, waveMax, teamMax, mapID, funcLineID, lineupID), 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)})
+ turnFight.sortActionQueue()
+ turnFight.startFight()
+
+ # 挑战boss无中间过程,每次执行直接挑战一队结果
+ __processTurnFight(turnFight.guid)
return
-def DoTurnFightProcess(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick):
- ## 执行回合制战斗的完整流程
+def __doMainFight(curPlayer, tick):
+ ## 主线执行战斗
- #if curPlayer.GetSightLevel() != curPlayer.GetID():
- # PlayerControl.SetPlayerSightLevel(curPlayer, curPlayer.GetID())
-
- SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_Start)
+ # 限制请求CD
+ #if not GameWorld.GetGameWorld().GetDebugLevel():
+ key = "MainFightReqTick"
+ lastTick = curPlayer.GetDictByKey(key)
+ if lastTick and tick - lastTick <= 1000:
+ GameWorld.DebugLog("主线战斗请求CD中")
+ return
+ curPlayer.SetDict(key, tick)
- tagPlayerInfo = {}
- if tagType == ChConfig.TurnBattle_TagType_Player and tagID >= 10000 :
- tagPlayer = GameWorld.GetMapCopyPlayerManager().FindPlayerByID(tagID)
- if tagPlayer:
- tagPlayerInfo = __GetPlayerInfo(tagPlayer)
+ mainFightMgr = GetMainFightMgr(curPlayer)
+ turnFight = mainFightMgr.turnFight
+
+ isLevelBoss = mainFightMgr.isLevelBoss()
+ 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))
+
+ mainFightMgr.nextTeam = False
+ turnFight.resetTurn({"teamNum":teamNum})
+ # 切换小队时,玩家阵容不需要处理,保留状态
+ turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(lineupID)})
+ turnFight.sortActionQueue()
+ turnFight.startFight()
+
+ if mainFightMgr.isLevelBoss():
+ # 每次处理一小队的完整战斗,相当于一次完整战报
+ __processTurnFight(turnFight.guid)
+ return
else:
- SendToGameServer_TurnFight(curPlayer, "TurnFightTagPlayerInfo", [mapID, funcLineID, tagType, tagID, valueList])
- return
-
- DoTrunFight(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick, tagPlayerInfo)
-
- SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_Over)
- return
-
-def __GetPlayerInfo(curPlayer):
- infoDict = {
- "Name":curPlayer.GetPlayerName(),
- "Job":curPlayer.GetJob(),
- "LV":curPlayer.GetLV(),
- "RealmLV":curPlayer.GetOfficialRank(),
- "MaxHP":GameObj.GetMaxHP(curPlayer),
- "FightPower":PlayerControl.GetFightPower(curPlayer),
- }
- return infoDict
-
-def DoTrunFightVSPlayer(curPlayer, tagPlayerID, callData, tagPlayerInfo):
- tagType = ChConfig.TurnBattle_TagType_Player
- tagID = tagPlayerID
- mapID, funcLineID, valueList = callData
- if tagPlayerInfo and curPlayer.GetPlayerID() != tagPlayerID:
- tick = GameWorld.GetGameWorld().GetTick()
- DoTrunFight(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick, tagPlayerInfo)
- SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_Over)
- return
-
-def DoTrunFight(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick, tagInfo=None):
- if not tagID:
+ __doMainLevelWave(curPlayer, False)
return
- if not tagInfo:
- tagInfo = {}
- playerID = curPlayer.GetPlayerID()
- GameWorld.DebugLog("回合战斗: mapID=%s,funcLineID=%s,tagType=%s,tagID=%s,valueList=%s,tagInfo=%s"
- % (mapID, funcLineID, tagType, tagID, valueList, tagInfo), playerID)
- factionSyncInfoA = __GetPlayerInfo(curPlayer)
- factionSyncInfoB = tagInfo
- SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_PrepareOK, msg=[factionSyncInfoA, factionSyncInfoB])
+ if isLevelBoss:
+ ## 关卡boss是一次性处理完的,一般不可能走到这里,这边做下防范
+ return
- turnNum, turnMax = 1, 15
+ # 以下均是处理关卡小怪分段实时战斗
- curFightPower = PlayerControl.GetFightPower(curPlayer)
- tagFightPower = tagInfo.get("FightPower", 0)
- isWin = 1 if curFightPower >= tagFightPower else 0
- GameWorld.DebugLog(" 战斗结果: isWin=%s,curFightPower=%s,tagFightPower=%s" % (isWin, curFightPower, tagFightPower), playerID)
+ EntryLogic(turnFight)
- factionTotalHurtDict = {}
+ # 小怪战斗,每次消耗1个战锤
+ fightPoint = max(curPlayer.GetFightPoint(), 1) # 主线战斗消耗倍值,默认1
- playbackID = 0 # 战斗回放ID,可根据该ID查看回放
+ # 按阵营阵容执行顺序,逐个遍历
+ doCnt = 0
+ doMax = (PosNumMax + 2) * len(turnFight.actionSortList) # 防止死循环,做最大循环次数限制 = (最大位置数 + 主公、红颜位置)*行动阵容数
+ overLineupList = [] # 本回合已经结束行动的阵容列表 [(faction, num), ...], 所有阵容全部结束代表本回合结束
- # 战斗结束后处理
- fightRet = [isWin, turnNum, turnMax, factionTotalHurtDict, playbackID]
-
- needSendGameServer, awardItemList, overInfoEx = False, [], {}
- overRet = FBLogic.OnTurnFightOver(curPlayer, mapID, funcLineID, tagType, tagID, valueList, fightRet)
- if overRet != None:
- needSendGameServer, awardItemList, overInfoEx = overRet
+ batObjMgr = BattleObj.GetBatObjMgr()
+ turnNum = turnFight.turnNum
+ GameWorld.DebugLog("turnNum=%s,doMax=%s,actionIndex=%s,%s" % (turnNum, doMax, turnFight.actionIndex, turnFight.actionSortList))
+ while doCnt < doMax and len(overLineupList) < len(turnFight.actionSortList):
+ doCnt += 1
+ turnNum = turnFight.turnNum
+ # 执行回合开始逻辑
+ if turnFight.turnStart < turnNum:
+ GameWorld.DebugLog("执行行动: turnNum=%s, 回合开始" % (turnFight.turnNum))
+ turnFight.syncState(FightState_Fighting)
+ if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
+ GameWorld.DebugLog("回合开始时战锤不足!")
+ return
+ turnFight.turnStart = turnNum
+ turnFight.actionIndex = 0
+ for faction, num in turnFight.actionSortList:
+ batFaction = turnFight.getBatFaction(faction)
+ batLineup = batFaction.getBatlineup(num)
+ batLineup.actionNum = ActionNumStart
+ for objID in batLineup.posObjIDDict.values():
+ batObj = batObjMgr.getBatObj(objID)
+ TurnFightObjPerTurnStart(turnFight, batObj, turnNum)
+
+ if turnFight.actionIndex >= len(turnFight.actionSortList):
+ turnFight.actionIndex = 0
+
+ playerHeroAtk = False # 玩家阵容行动标记
+ faction, num = turnFight.actionSortList[turnFight.actionIndex]
+ batFaction = turnFight.getBatFaction(faction)
+ batLineup = batFaction.getBatlineup(num)
+ if batLineup.actionNum > max(batLineup.posObjIDDict):
+ if (faction, num) not in overLineupList:
+ overLineupList.append((faction, num))
+
+ turnFight.actionIndex += 1
+ continue
- if needSendGameServer or mapID in ChConfig.Def_TFMapID_SendToGameServer:
- SendToGameServer_TurnFight(curPlayer, "TurnFightOver", [mapID, funcLineID, tagType, tagID, valueList, fightRet, awardItemList])
+ if faction == ChConfig.Def_FactionA:
+ if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
+ GameWorld.DebugLog("战锤不足!")
+ return
+ GameWorld.DebugLog("执行行动: turnNum=%s,faction=%s,num=%s,actionNum=%s" % (turnFight.turnNum, faction, num, batLineup.actionNum))
- overMsg = {"isWin":isWin, FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(awardItemList), "totalHurt":0}
- playbackID and overMsg.update({"playbackID":playbackID})
- overInfoEx and overMsg.update(overInfoEx)
- SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_Award, turnNum, turnMax, overMsg)
+ # 主公
+ if batLineup.actionNum == -1:
+ pass
+
+ # 红颜
+ elif batLineup.actionNum == 0:
+ pass
+
+ # 武将
+ elif batLineup.actionNum > 0:
+ for posNum in range(batLineup.actionNum, PosNumMax + 1):
+ batLineup.actionNum = posNum
+ if posNum not in batLineup.posObjIDDict:
+ continue
+ objID = batLineup.posObjIDDict[posNum]
+ batObj = batObjMgr.getBatObj(objID)
+ if not OnObjAction(turnFight, batObj):
+ continue
+
+ if faction == ChConfig.Def_FactionA:
+ playerHeroAtk = True
+
+ break
+
+ turnFight.actionIndex += 1
+ batLineup.actionNum += 1
+ if batLineup.actionNum > max(batLineup.posObjIDDict):
+ GameWorld.DebugLog("该阵容本回合已经全部行动完了: turnNum=%s,faction=%s,num=%s,actionNum=%s" % (turnFight.turnNum, faction, num, batLineup.actionNum))
+ if (faction, num) not in overLineupList:
+ overLineupList.append((faction, num))
+
+ if turnFight.checkOverByKilled():
+ break
+
+ if playerHeroAtk:
+ # 玩家武将有行动则停止,一段段执行
+ GameWorld.DebugLog("玩家武将有行动则停止!")
+ break
+
+ if turnFight.winFaction:
+ return
+
+ GameWorld.DebugLog("turnNum=%s,doCnt=%s,overLineupList=%s" % (turnNum, doCnt, overLineupList))
+ if len(overLineupList) >= len(turnFight.actionSortList):
+ GameWorld.DebugLog("执行行动: turnNum=%s, 回合结束" % (turnFight.turnNum))
+ if turnFight.turnEnd < turnNum:
+ if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint):
+ GameWorld.DebugLog("回合结束时战锤不足!")
+ return
+ turnFight.turnEnd = turnNum
+ for faction, num in turnFight.actionSortList:
+ batFaction = turnFight.getBatFaction(faction)
+ batLineup = batFaction.getBatlineup(num)
+ for objID in batLineup.posObjIDDict.values():
+ pass
+
+ if turnFight.checkOverByKilled():
+ return
+
+ if turnNum < turnFight.turnMax:
+ turnFight.turnNum += 1
+ else:
+ OnTurnAllOver(turnFight.guid)
+
return
-def SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, state, turnNum=0, turnMax=0, msg=""):
+def __processTurnFight(guid):
+ ## 一次性处理完一个小队的战斗
+ turnFight = GetTurnFightMgr().getTurnFight(guid)
+ curPlayer = turnFight.curPlayer
+ turnMax = turnFight.turnMax
+ EntryLogic(turnFight)
+ batObjMgr = BattleObj.GetBatObjMgr()
+ for turnNum in range(1, turnMax + 1):
+ turnFight.turnNum = turnNum
+ GameWorld.DebugLog("【----- 回合制战斗轮次: %s -----】" % turnNum)
+ if curPlayer:
+ turnFight.syncState(FightState_Fighting)
+
+ # 回合开始
+ for faction, num in turnFight.actionSortList:
+ GameWorld.DebugLog("回合开始逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
+ batFaction = turnFight.getBatFaction(faction)
+ batLineup = batFaction.getBatlineup(num)
+ batLineup.actionNum = 1
+ for objID in batLineup.posObjIDDict.values():
+ batObj = batObjMgr.getBatObj(objID)
+ TurnFightObjPerTurnStart(turnFight, batObj, turnNum)
+
+ # 主公
+ for faction, num in turnFight.actionSortList:
+ GameWorld.DebugLog("主公逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
+ batFaction = turnFight.getBatFaction(faction)
+ batLineup = batFaction.getBatlineup(num)
+
+ # 红颜
+ for faction, num in turnFight.actionSortList:
+ GameWorld.DebugLog("红颜逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
+ batFaction = turnFight.getBatFaction(faction)
+ batLineup = batFaction.getBatlineup(num)
+
+ if turnFight.checkOverByKilled():
+ break
+
+ # 武将
+ doMax = PosNumMax * len(turnFight.actionSortList)
+ doCnt = 0
+ while doCnt < doMax and turnFight.actionIndex < len(turnFight.actionSortList):
+ doCnt += 1
+ faction, num = turnFight.actionSortList[turnFight.actionIndex]
+ batFaction = turnFight.getBatFaction(faction)
+ batLineup = batFaction.getBatlineup(num)
+ for posNum in range(batLineup.actionNum, PosNumMax + 1):
+ batLineup.actionNum = posNum + 1
+ if posNum not in batLineup.posObjIDDict:
+ continue
+ objID = batLineup.posObjIDDict[posNum]
+ batObj = batObjMgr.getBatObj(objID)
+ if not OnObjAction(turnFight, batObj):
+ continue
+
+ break
+
+ if turnFight.actionIndex >= len(turnFight.actionSortList) - 1:
+ turnFight.actionIndex = 0
+ else:
+ turnFight.actionIndex += 1
+
+ # 回合结束
+ for faction, num in turnFight.actionSortList:
+ GameWorld.DebugLog("回合结束逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num))
+ batFaction = turnFight.getBatFaction(faction)
+ batLineup = batFaction.getBatlineup(num)
+ for objID in batLineup.posObjIDDict.values():
+ pass
+
+ if turnFight.checkOverByKilled():
+ break
+
+ if not turnFight.winFaction:
+ OnTurnAllOver(turnFight.guid)
+ return
+
+def GetObjName(batObj):
+ faction = batObj.faction
+ num = batObj.lineupNum
+ posNum = batObj.posNum
+ heroID = batObj.heroID
+ if not heroID:
+ heroID = batObj.npcID
+ objName = GameWorld.CodeToGbk(batObj.GetName())
+ return "%s%s-%s [%s-%s] %s" % ("A" if faction == ChConfig.Def_FactionA else "B", num, posNum, batObj.GetID(), heroID, objName)
+
+def EntryLogic(turnFight):
+ ## 执行进场逻辑
+ if turnFight.enterLogic:
+ return
+ GameWorld.DebugLog("执行进场逻辑...")
+
+ turnFight.enterLogic = True
+ return
+
+def TurnFightObjPerTurnStart(turnFight, batObj, turnNum):
+ ## 回合制战斗实例 - 每回合开始时处理
+ if not batObj:
+ return
+
+ if batObj.GetHP() <= 0:
+ return
+
+ curID = batObj.GetID()
+ buffMgr = batObj.GetBuffManager()
+ GameWorld.DebugLog("更新buff: curID=%s,buffCount=%s" % (curID, buffMgr.GetBuffCount()))
+ for index in range(buffMgr.GetBuffCount())[::-1]:
+ buff = buffMgr.GetBuffByIndex(index)
+ curRemainTime = buff.GetRemainTime()
+ if not curRemainTime:
+ # 永久buff不处理
+ continue
+ buffID = buff.GetBuffID()
+ skillID = buff.GetSkillID()
+ updRemainTime = curRemainTime - 1
+ GameWorld.DebugLog(" 更新buff剩余回合数: buffID=%s,skillID=%s,updRemainTime=%s" % (buffID, skillID, updRemainTime))
+ if updRemainTime > 0:
+ buff.SetRemainTime(updRemainTime)
+ TurnBuff.SyncBuffRefresh(turnFight, batObj, buff)
+ else:
+ TurnBuff.DoBuffDel(turnFight, batObj, buff)
+
+# SetTimeline(gameObj, turnNum, 0)
+# # 重置连击、反击数
+# gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnComboNum, 0)
+# gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnAtkBackNum, 0)
+# gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnParryNum, 0)
+# gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnMissNum, 0)
+# gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnAtkAddXPCount, 0)
+# gameObj.SetDict(ChConfig.Def_Obj_Dict_TurnXPUseState, 0)
+# gameObj.SetDict(ChConfig.Def_PlayerKey_AttrFaintCD, 0) # 击晕CD
+#
+# objType = gameObj.GetGameObjType()
+# objID = gameObj.GetID()
+# objName = GetObjName(gameObj)
+# GameWorld.DebugLog("ObjPerTurnStart: 回合%s, %s objType-ID-HP(%s-%s-%s)" % (turnNum, objName, objType, objID, GameObj.GetHP(gameObj)))
+#
+# # 每回合开始减技能CD
+# skillManager = gameObj.GetSkillManager()
+# for i in range(skillManager.GetSkillCount()):
+# skill = skillManager.GetSkillByIndex(i)
+# remainTime = skill.GetRemainTime()
+# if not remainTime:
+# continue
+# skillID = skill.GetSkillID()
+# updRemainTime = max(0, remainTime - ChConfig.Def_PerTurnTick)
+# skill.SetRemainTime(updRemainTime)
+# GameWorld.DebugLog(" skillID=%s,remainTime=%s,updRemainTime=%s" % (skillID, remainTime, updRemainTime))
+#
+# # 刷新定时处理的buff效果
+# SkillShell.ProcessPersistBuff(gameObj, tick)
+#
+# PassiveBuffEffMng.OnPassiveSkillTrigger(gameObj, tagObj, None, ChConfig.TriggerType_TurnNum, tick)
+#
+# __logGameObjAttr(gameObj)
+ return
+
+def AddTurnObjCureHP(curObj, srcObj, addValue, cureHP, skillID=0):
+ ## 回合对象添加治疗值
+ # @param curObj: 获得治疗的对象
+ # @param srcObj: 来自谁的治疗
+ # @param addValue: 治疗值
+ # @param cureHP: 实际回血量
+ if not srcObj:
+ return
+ curID = curObj.GetID()
+ srcID = srcObj.GetID()
+ updStatValue = srcObj.StatCureValue(cureHP)
+ GameWorld.DebugLog(" 统计治疗: curID=%s,srcID=%s,skillID=%s,addValue=%s,cureHP=%s,updStatValue=%s"
+ % (curID, srcID, skillID, addValue, cureHP, updStatValue))
+
+ return
+
+def AddTurnObjHurtValue(curBatObj, tagBatObj, hurtValue, lostHP, curSkill=None, isBounce=False):
+ ## 回合对象添加伤害值
+ # @param isBounce: 是否反弹伤害
+ curID = curBatObj.GetID()
+ tagID = tagBatObj.GetID()
+ skillID = curSkill.GetSkillID() if curSkill else 0
+ 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))
+
+ if tagBatObj:
+ updStatValue = tagBatObj.StatDefValue(lostHP)
+ 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))
+
+ else:
+ # 如换血类技能,自残的伤害不算输出
+ GameWorld.DebugLog(" 自残: curID=%s,tagID=%s,skillID=%s,hurtValue=%s,lostHP=%s,curHP=%s"
+ % (curID, tagID, skillID, hurtValue, lostHP, curBatObj.GetHP()))
+ return
+
+def OnObjAction(turnFight, curBatObj):
+ ## 战斗单位行动
+ if not curBatObj:
+ return
+
+ curHP = curBatObj.GetHP()
+ objID = curBatObj.GetID()
+ if curHP <= 0:
+ return
+
+ turnNum = turnFight.turnNum
+ objName = GetObjName(curBatObj)
+
+ # 是否可行动状态判断
+ canAction = True
+
+ if not canAction:
+ GameWorld.DebugLog("★回合%s %s 当前状态不可行动!" % (turnNum, objName))
+ return
+
+ atk = curBatObj.GetAtk()
+ curXP = curBatObj.GetXP()
+ GameWorld.DebugLog("★回合%s %s 行动 : atk=%s,curHP=%s/%s,curXP=%s" % (turnNum, objName, atk, curHP, curBatObj.GetMaxHP(), curXP))
+ turnFight.syncObjAction(turnNum, objID)
+
+ xpMax = IpyGameDataPY.GetFuncCfg("AngerXP", 2)
+ skillManager = curBatObj.GetSkillManager()
+ useSkillList = []
+ #GameWorld.DebugLog('skillCount=%s' % skillManager.GetSkillCount(), npcID)
+ for index in range(0, skillManager.GetSkillCount()):
+ useSkill = skillManager.GetSkillByIndex(index)
+ if not useSkill:
+ continue
+ if useSkill.GetFuncType() in [ChConfig.Def_SkillFuncType_AtkbackSkill]:
+ #基础普攻不能主动释放,目前仅用于反击
+ continue
+ #被动技能无法使用
+ if SkillCommon.isPassiveSkill(useSkill):
+ continue
+ #还在冷却时间内无法释放
+ if useSkill.GetRemainTime():
+ continue
+ skillID = useSkill.GetSkillID()
+ # 常规攻击优先xp
+ if SkillCommon.isAngerSkill(useSkill):
+ if curXP < xpMax:
+ continue
+ useCnt = -1 # xp技能优先释放
+ else:
+ useCnt = curBatObj.GetSkillUseCnt(skillID)
+ useSkillList.append([useCnt, skillID, useSkill])
+
+ useSkillList.sort() # 按使用次数优先升序排,使用次数低的优先判断使用
+ #GameWorld.DebugLog(' 技能使用顺序 = useSkillList%s' % str(useSkillList), npcID)
+
+ for useInfo in useSkillList:
+ useSkill = useInfo[-1]
+ if TurnSkill.OnUseSkill(turnFight, curBatObj, useSkill):
+ return True
+
+ return
+
+def DoAttack(curBatObj, tagBatObj, tick, turnBattleType=ChConfig.TurnBattleType_Normal, useSkill=None):
+# curID = curBatObj.GetID()
+# npcID = curBatObj.GetNPCID()
+# objName = GetObjName(curBatObj)
+# GameWorld.DebugLog(" ● %s DoAttack: curID=%s,,turnBattleType=%s" % (objName, curID, turnBattleType))
+#
+# atkOK = False
+# curBatObj.SetDict(ChConfig.Def_Obj_Dict_TurnBattleType, turnBattleType)
+#
+# if turnBattleType == ChConfig.TurnBattleType_AtkBack:
+# if not tagBatObj:
+# return
+# skillManager = curBatObj.GetSkillManager()
+# for index in range(0, skillManager.GetSkillCount()):
+# skill = skillManager.GetSkillByIndex(index)
+# if not skill:
+# continue
+# if skill.GetFuncType() == ChConfig.Def_SkillFuncType_AtkbackSkill:
+# useSkill = skill
+# break
+# atkOK = SkillShell.DoLogic_UseSkill(curBatObj, tagBatObj, useSkill, tick)
+# elif turnBattleType == ChConfig.TurnBattleType_Combo:
+# if not tagBatObj:
+# return
+# atkOK = SkillShell.DoLogic_UseSkill(curBatObj, tagBatObj, useSkill, tick)
+# else:
+# curXP = GameObj.GetXP(curBatObj)
+# xpMax = IpyGameDataPY.GetFuncCfg("AngerXP", 2)
+# skillManager = curBatObj.GetSkillManager()
+# useSkillList = []
+# #GameWorld.DebugLog('skillCount=%s' % skillManager.GetSkillCount(), npcID)
+# for index in range(0, skillManager.GetSkillCount()):
+# useSkill = skillManager.GetSkillByIndex(index)
+# if not useSkill:
+# continue
+# if useSkill.GetFuncType() in [ChConfig.Def_SkillFuncType_AtkbackSkill]:
+# #基础普攻不能主动释放,目前仅用于反击
+# continue
+# #被动技能无法使用
+# if SkillCommon.isPassiveSkill(useSkill):
+# continue
+# #还在冷却时间内无法释放
+# if useSkill.GetRemainTime():
+# continue
+# skillID = useSkill.GetSkillID()
+# # 常规攻击优先xp
+# if SkillCommon.isAngerSkill(useSkill) and turnBattleType == ChConfig.TurnBattleType_Normal:
+# if curXP < xpMax:
+# continue
+# useCnt = -1 # xp技能优先释放
+# else:
+# useCnt = curBatObj.GetDictByKey(ChConfig.Def_NPC_Dict_SkillUseCnt % skillID) # 该技能已使用次数
+# useSkillList.append([useCnt, index, skillID, useSkill])
+#
+# useSkillList.sort() # 按使用次数优先升序排,使用次数低的优先判断使用
+# #GameWorld.DebugLog(' 技能使用顺序 = useSkillList%s' % str(useSkillList), npcID)
+#
+# for useInfo in useSkillList:
+# useSkill = useInfo[-1]
+# #skillID = useSkill.GetSkillID()
+# atkOK, tagBatObj = OnUseSkill(curBatObj, useSkill, tick)
+# if atkOK:
+# break
+#
+# curBatObj.SetDict(ChConfig.Def_Obj_Dict_TurnBattleType, 0) # 无论攻击成功与否都重置战斗类型
+#
+# tagID = 0
+# tagHP = 0
+# if tagObj:
+# tagID = tagObj.GetID()
+# tagHP = GameObj.GetHP(tagObj)
+# GameWorld.DebugLog(' atkOK=%s,tagID=%s,tagHP=%s' % (atkOK, tagID, tagHP), npcID)
+# if not atkOK:
+# return
+#
+# if not tagObj:
+# return
+#
+# if GameObj.GetFaction(curNPC) == GameObj.GetFaction(tagObj):
+# # 同阵营直接返回,不处理连击、反击
+# return True
+#
+# curHP = GameObj.GetHP(curNPC)
+# tagHP = GameObj.GetHP(tagObj)
+# tagID = tagObj.GetID()
+# GameWorld.DebugLog(" curID-HP=(%s-%s),tagID-HP=(%s-%s)" % (curID, curHP, tagID, tagHP))
+# if tagHP <= 0 or curHP <= 0:
+# return True
+#
+# # 反击,反击可打断连击,所以优先判断
+# if CanAtkBack(curNPC, tagObj):
+# DoAttack(tagObj, curNPC, tick, ChConfig.TurnBattleType_AtkBack)
+# return True
+#
+# # 连击
+# if CanCombo(curNPC, tagObj):
+# DoAttack(curNPC, tagObj, tick, ChConfig.TurnBattleType_Combo, useSkill)
+#
+ return True
+
+def CanAtkBack(atkObj, defObj):
+ ## 可否反击
+ posNum = atkObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo) % 100
+ if posNum <= 0:
+ GameWorld.DebugLog(" 被非主战单位攻击时无法触发反击: atkID=%s,posNum=%s" % (atkObj.GetID(), posNum))
+ return False
+ atkDistType = AttackCommon.GetAtkDistType(defObj)
+ if atkDistType == ChConfig.AtkDistType_Long:
+ if not IpyGameDataPY.GetFuncCfg("ParryCfg", 2):
+ GameWorld.DebugLog(" 远程单位不可反击: defID=%s" % (defObj.GetID()))
+ return False
+ defAtkBackRate = GameObj.GetAtkBackRate(defObj) # 防方反击率
+ atkBackNum = defObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnAtkBackNum) # 已反击次数
+ if atkBackNum > 10:
+ # 内置最高反击数防范
+ return False
+ if atkBackNum > 0:
+ validPerList = IpyGameDataPY.GetFuncEvalCfg("TurnFight", 4)
+ vaildPer = validPerList[atkBackNum - 1] if len(validPerList) >= atkBackNum else 0
+ defAtkBackRate = int(defAtkBackRate * vaildPer / 100.0)
+
+ atkAtkBackDefRate = GameObj.GetAtkBackDefRate(atkObj) # 攻方抵抗反击率
+ atkBackRate = max(0, defAtkBackRate - atkAtkBackDefRate)
+ if atkBackRate <= 0 or not GameWorld.CanHappen(atkBackRate):
+ GameWorld.DebugLog(" 无法反击: defID=%s,atkBackNum=%s,atkBackRate=%s=(defAtkBackRate=%s - atkAtkBackDefRate=%s)"
+ % (defObj.GetID(), atkBackNum, atkBackRate, defAtkBackRate, atkAtkBackDefRate))
+ return False
+ GameWorld.DebugLog(" 可以反击: defID=%s,atkBackNum=%s,atkBackRate=%s=(defAtkBackRate=%s - atkAtkBackDefRate=%s)"
+ % (defObj.GetID(), atkBackNum, atkBackRate, defAtkBackRate, atkAtkBackDefRate))
+ return True
+
+def CanCombo(atkObj, defObj):
+ ## 可否连击
+
+ atkComboRate = GameObj.GetComboRate(atkObj) # 攻方连击率
+ comboNum = atkObj.GetDictByKey(ChConfig.Def_Obj_Dict_TurnComboNum) # 已连击次数
+ if comboNum > 10:
+ # 内置最高连击数防范
+ return False
+ if comboNum > 0:
+ validPerList = IpyGameDataPY.GetFuncEvalCfg("TurnFight", 3)
+ vaildPer = validPerList[comboNum - 1] if len(validPerList) >= comboNum else 0
+ atkComboRate = int(atkComboRate * vaildPer / 100.0)
+
+ defComboReduce = GameObj.GetComboDefRate(defObj) # 防方抵抗连击率
+ comboRate = max(0, atkComboRate - defComboReduce)
+ if comboRate <= 0 or not GameWorld.CanHappen(comboRate):
+ GameWorld.DebugLog(" 无法连击: atkID=%s,comboNum=%s,comboRate=%s=(atkComboRate=%s - defComboReduce=%s)"
+ % (atkObj.GetID(), comboNum, comboRate, atkComboRate, defComboReduce))
+ return False
+ GameWorld.DebugLog(" 可以连击: atkID=%s,comboNum=%s,comboRate=%s=(atkComboRate=%s - defComboReduce=%s)"
+ % (atkObj.GetID(), comboNum, comboRate, atkComboRate, defComboReduce))
+ return True
+
+#def GetEnemyObj(curNPC, ruleType=0):
+# ## 获取一个敌对单位,针对所有敌对阵容主战单位,优先选择对位阵容单位,仅限活着的单位
+# # @param ruleType: 选择规则,默认0任意,1-最低血量;2-最高血量
+# objID = curNPC.GetID()
+# faction = GameObj.GetFaction(curNPC)
+# posInfo = curNPC.GetDictByKey(ChConfig.Def_Obj_Dict_TurnFightPosInfo)
+# num = posInfo / 100 # 阵容编号
+# posNum = posInfo % 100 # 所在阵容站位
+# turnFight = GetTurnFightMgr().getTurnFight(curNPC.GetTFGUID())
+# if not turnFight:
+# return
+#
+# tagBatFaction = turnFight.getBatFaction(Def_FactionB if faction == Def_FactionA else Def_FactionA)
+#
+# tagLineupNumList = [num] # 目标阵容选择顺序,优先对位阵容,再其他阵容
+# if num in tagBatFaction.lineupDict:
+# tagLineupNumList = [num]
+# for tagNum in tagBatFaction.lineupDict.keys():
+# if tagNum not in tagLineupNumList:
+# tagLineupNumList.append(tagNum)
+#
+# batObjMgr = BattleObj.GetBatObjMgr()
+# for tagNum in tagLineupNumList:
+# tagLineup = tagBatFaction.getBatlineup(tagNum)
+# tagPosNumList = [posNum] # 优先对位位置,再其他位置按顺序遍历
+# pNumList = tagLineup.posObjIDDict.keys()
+# pNumList.sort()
+# for pNum in pNumList:
+# if pNum > 0 and pNum not in tagPosNumList:
+# tagPosNumList.append(pNum)
+# for pNum in tagPosNumList:
+# batObj = batObjMgr.getBatObj(objID)
+# if not batObj:
+# continue
+# if batObj.GetHP( )<= 0:
+# continue
+# return batObj
+# return
+
+def SetObjKilled(turnFight, gameObj, killer=None, useSkill=None):
+ objID = gameObj.GetID()
+ GameWorld.DebugLog(" %s 回合战斗主体被击杀: curID=%s" % (GetObjName(gameObj), objID))
+ gameObj.SetHP(0)
+ killerObjID = killer.GetID() if killer else 0
+ skillID = useSkill.GetSkillID() if useSkill else 0
+
+ clientPack = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagMCTurnFightObjDead)
+ clientPack.ObjID = objID
+ clientPack.KillerObjID = killerObjID
+ clientPack.SkillID = skillID
+ turnFight.addBatPack(clientPack)
+ return True
+
+def OnTurnAllOver(guid):
+ ## 所有回合已经全部执行完毕
+ GameWorld.DebugLog("所有回合结束")
+ turnFight = GetTurnFightMgr().getTurnFight(guid)
+ if not turnFight:
+ return
+
+ if turnFight.winFaction:
+ return
+
+ if turnFight.playerID:
+ # 玩家发起的,未击杀对方,算玩家输
+ turnFight.winFaction = ChConfig.Def_FactionB
+ else:
+ # 系统场次,按一定规则来,这里先随机
+ turnFight.winFaction = random.choice([ChConfig.Def_FactionA, ChConfig.Def_FactionB])
+
+ DoTurnFightOver(guid)
+ return
+
+def DoTurnFightOver(guid):
+ ## 执行回合战斗结算逻辑
+ tfMgr = GetTurnFightMgr()
+ turnFight = tfMgr.getTurnFight(guid)
+ if not turnFight:
+ return
+
+ turnFight.costTime = time.time() - turnFight.startTime
+ winFaction = turnFight.winFaction
+ GameWorld.DebugLog("--- 战斗结束处理 --- %s, winFaction=%s, costTime=%ss" % (guid, winFaction, turnFight.costTime))
+
+ # 统计明细
+ batObjMgr = BattleObj.GetBatObjMgr()
+ statInfo = {}
+ for faction in turnFight.factionDict.keys():
+ if str(faction) not in statInfo:
+ statInfo[str(faction)] = {}
+ facStatInfo = statInfo[str(faction)]
+ batFaction = turnFight.getBatFaction(faction)
+ for num in batFaction.lineupDict.keys():
+ if str(num) not in facStatInfo:
+ facStatInfo[str(num)] = {}
+ lineupStatInfo = facStatInfo[str(num)]
+ batLineup = batFaction.getBatlineup(num)
+ GameWorld.DebugLog("阵容明细: faction=%s,num=%s" % (faction, num))
+ for posNum, objID in batLineup.posObjIDDict.items():
+ batObj = batObjMgr.getBatObj(objID)
+ if not batObj:
+ continue
+ objID = batObj.GetID()
+ npcID = batObj.GetNPCID()
+ heroID = batObj.GetHeroID()
+ atkHurt = batObj.hurtStat
+ defHurt = batObj.defStat
+ cureHP = batObj.cureStat
+ GameWorld.DebugLog(" Pos:%s ID=%s-%s-%s,,HP=%s/%s, 输出=%s,承伤=%s,治疗=%s"
+ % (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)}
+ turnFight.syncState(FightState_Award, overMsg)
+
+ # 除了主线外,其余战斗结束后均直接删除
+ if not turnFight.playerID:
+ tfMgr.delTurnFight(guid)
+ return
+
+def OnOver_MainLevel(curPlayer, isWin, awardItemList):
+ ## 战斗结束额外处理 - 主线关卡
if not curPlayer:
return
- clientPack = ChPyNetSendPack.tagMCTurnFightState()
- clientPack.Clear()
- clientPack.MapID = mapID
- clientPack.FuncLineID = funcLineID
- clientPack.TagType = tagType
- clientPack.TagID = tagID
- clientPack.State = state
- clientPack.TurnNum = turnNum
- clientPack.TurnMax = turnMax
- clientPack.Msg = str(msg)
- clientPack.Len = len(clientPack.Msg)
- NetPackCommon.SendFakePack(curPlayer, clientPack)
+ 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
+#
+#struct tagCSTurnFightReportView
+#{
+# tagHead Head;
+# char GUID[40]; //战报guid
+#};
+def OnTurnFightReportView(index, clientData, tick):
return
--
Gitblit v1.8.0