From cc207773cbedb51c20300a87c62529ace416b086 Mon Sep 17 00:00:00 2001 From: hxp <ale99527@vip.qq.com> Date: 星期五, 19 九月 2025 19:23:35 +0800 Subject: [PATCH] 129 【战斗】战斗系统-服务端(无敌支持,免疫伤害、dot、控制;小怪技能;) --- ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py | 1762 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 1,632 insertions(+), 130 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..111c080 100644 --- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py +++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py @@ -10,19 +10,43 @@ # @version 1.0 # # 详细描述: 回合制攻击逻辑,均使用NPC实例作为战斗主体 +# 实时战斗: 仅用于主线,每个玩家最多仅允许存在一个实时战斗 +# 由前端控制战斗流程,后端进行计算 +# 瞬时战斗: 用于非主线外的各种战斗,如副本PVE挑战,PVP挑战等,一样仅有一个瞬时战斗, +# 一次性处理完,后端控制整体战斗流程及计算,前端仅做战报解析表现战斗过程 # #------------------------------------------------------------------------------- #"""Version = 2023-11-30 15:30""" #------------------------------------------------------------------------------- import ChConfig +import PlayerTask +import PlayerViewCache import ChPyNetSendPack import NetPackCommon import PlayerControl import GameWorld -import GameObj -import FBCommon +import IpyGameDataPY +import PlayerOnline +import NPCCommon +import ShareDefine +import PyGameData +import SkillCommon +import BattleObj +import TurnPassive +import TurnSkill +import TurnBuff +import CommFunc +import ObjPool import FBLogic + +import random +import time +import json + +TimelineSet = 10000 # 单回合最大时间轴 +PosNumMax = 7 # 最大站位编号 +ActionNumStart = -1 # 起始行动位置编号,一般是从1开始,如果有加主公、红颜等则扣除相应位置值,如从0或-1开始 # 回合战斗流程状态 ( @@ -32,21 +56,800 @@ FightState_FightEnd, # 3 战斗结束 FightState_Award, # 4 结算奖励 FightState_Over, # 5 结束状态,无特殊意义,仅代表所有处理结束了,与Start对应 -) = range(6) +FightState_Rest, # 6 结束并休息,一般是前端主动退出并休息的 +) = range(7) + +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.lingshouObjIDDict = {} # 灵兽战斗单位 {位置编号:batObjID, ...} + self.beautyObjIDDict = {} # 红颜战斗单位 {位置编号:batObjID, ...} + 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): + ## 清除阵容 + batObjMgr = BattleObj.GetBatObjMgr() + for objID in self.posObjIDDict.values(): + batObjMgr.delBatObj(objID) + for objID in self.lingshouObjIDDict.values(): + batObjMgr.delBatObj(objID) + for objID in self.beautyObjIDDict.values(): + batObjMgr.delBatObj(objID) + self.posObjIDDict = {} + self.lingshouObjIDDict = {} + self.beautyObjIDDict = {} + 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.winFaction = 0 # 本场战斗结束标记,获胜阵营,为0时代表未结束,所有小队打完或失败才有结果,0-未结束,>0-获胜的阵营 + self.isWin = False + 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.timeline = 0 # 时间轴节点 turnNum*1000+actionIndex*100++actionNum + self.startTime = 0 # 开始时间戳,支持毫秒小数 + self.costTime = 0 # 单场战斗总耗时,支持毫秒小数 + self._oneActionUseSkillCntDict = {} # 某对象行动开始后所有对象累计使用技能次数,用于单对象单次行动中限制每个对象的最高触发技能次数 {objID:useCnt, ...} + + # pve 多小队 - 一般只有PVE用到 + self.lineupIndex = 0 # 当前小队索引 + self.lineupIDList = [] # npc小队列表 + self.strongerLV = 0 # npc成长等级 + self.difficulty = 0 # npc难度 + + # pvp 目标 + self.tagPlayerID = 0 + self.tagViewCache = None + return + + def setTurnFight(self, mapID, funcLineID, turnMax, isNeedReport=False, msgDict={}): + ## 设置本场回合战斗设定 + self.mapID = mapID + self.funcLineID = funcLineID + self.turnMax = turnMax # 最大回合数 + self.isNeedReport = isNeedReport + self.setPVE() + self.setPVP() + self.msgDict = {} + self.nextTurnFight(msgDict) + return + + def setPVE(self, lineupIDList=[], strongerLV=0, difficulty=0): + self.lineupIndex = 0 + self.lineupIDList = lineupIDList + self.strongerLV = strongerLV + self.difficulty = difficulty + return + + def setPVP(self, tagPlayerID=0, tagViewCache=None): + self.tagPlayerID = tagPlayerID + self.tagViewCache = tagViewCache + return + + #def setPVPTeam(self): + # return + + def nextTurnFight(self, msgDict={}, resetByNextTeam=False): + ## 一般用于玩家发起的战斗,在需要保留玩家阵容属性及状态的情况下,重置回合进入下一场战斗 + self.turnNum = 1 + self.enterLogic = False + self.winFaction = 0 + self.isWin = False + self.msgDict.update(msgDict) + self.timeline = 0 + self.startTime = time.time() + self.costTime = 0 + if resetByNextTeam: + ResetByNextTeam(self) + 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): + ## 设置阵营阵容 + # @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(): + isPlayer = 1 if batLineup.ownerID else 0 # 玩家阵容优先攻击 + fightPower = batLineup.fightPower + sortValue = -(faction * 10 + num) + sortList.append([isPlayer, 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("阵容战力排序[isPlayer, fp, sortV, f, n]: %s" % sortList) + GameWorld.DebugLog("阵容行动顺序[f, n]: %s" % self.actionSortList) + return + + def getTurnNumStartTimelin(self, turnNum): return turnNum * TimelineSet + 0 # 每回合的时间节点起点 + def getTimeline(self): return self.timeline + def setTimeline(self, timeline, isEmpty=False): + '''回合战斗的时间轴节点 ,即第几回合开始,每个回合支持9999个行动节点 + @param turnNum: 第x回合 + ''' + self.timeline = timeline + GameWorld.DebugLog("[时间节点更新]: %s" % self.timeline) + if isEmpty: + # 空位置的节点可直接跳过 + return timeline + + OnTimelineChange(self) + return timeline + + 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.IsAlive(): + allKilled = False + break + + if allKilled: + self.winFaction = ChConfig.Def_FactionA if faction == ChConfig.Def_FactionB else ChConfig.Def_FactionB + DoTurnFightOver(self.guid) + return self.winFaction + + return 0 + + def exitFight(self, rest=False): + ## 退出战斗 + self.syncState(FightState_Over if not rest else FightState_Rest) + for batFaction in self.factionDict.values(): + batFaction.clearLineups() + self.state = -1 + return + + def startFight(self): + ## 准备就绪,开始战斗 + self.state = FightState_Start + self.turnNum = 1 + self.timeline = self.getTurnNumStartTimelin(self.turnNum) + 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) + if self.isNeedReport: + packBuff = clientPack.GetBuffer() + buffLen = len(packBuff) + GameWorld.DebugLog("回合战斗过程封包: %s, len:%s" % (headStr, buffLen)) + self.batBuffer += CommFunc.WriteWORD("", buffLen) + 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 + + def ResetOneActionUseSkillCnt(self): self._oneActionUseSkillCntDict = {} + def GetOneActionUseSkillCnt(self, objID): return self._oneActionUseSkillCntDict.get(objID, 0) + def SetOneActionUseSkillCnt(self, objID, useCnt): + self._oneActionUseSkillCntDict[objID] = useCnt + return useCnt + +class TurnFightMgr(): + ## 回合战斗管理器 + + def __init__(self): + self.turnFightDict = {} # {guid:TurnFight, ...} + return + + 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 + + def delTurnFight(self, guid): + turnFight = self.getTurnFight(guid) + if not turnFight: + return + turnFight.exitFight() + self.turnFightDict.pop(guid, None) + ObjPool.GetPoolMgr().release(turnFight) + 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.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 + +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 GetCacheLineupFightPower(tagViewCache, lineupID): + lineupInfo = GetCacheLineupInfo(tagViewCache, lineupID) + return lineupInfo.get("FightPower", 0) +def GetCacheLineupInfo(tagViewCache, lineupID): + ## 根据查看缓存获取阵容信息,一般是 GetPlayerLineupInfo 返回的结果 + plusDict = tagViewCache.GetPlusDict() + lineupDict = plusDict.get("Lineup", {}) + lineupInfo = lineupDict.get("%s" % lineupID, {}) + if not lineupInfo: + lineupInfo = lineupDict.get("%s" % ShareDefine.Lineup_Main, {}) + return lineupInfo + +def GetPlayerLineupFightPower(curPlayer, lineupID): + ## 获取玩家阵容战力,一般用于直接获取阵容战力记录用 + return GetPlayerLineup(curPlayer, lineupID).fightPower +def GetPlayerLineup(curPlayer, lineupID): + ## 获取玩家阵容 + olPlayer = PlayerOnline.GetOnlinePlayer(curPlayer) + lineup = olPlayer.GetLineup(lineupID) + if not lineup.lineupHeroDict: + # 为空时默认取主阵容 + lineup = olPlayer.GetLineup(ShareDefine.Lineup_Main) + return lineup + +def GetPlayerLineupInfo(curPlayer, lineupID): + ## 获取玩家阵容信息,可用于战斗或查看缓存,因为可能取玩家的缓存进行对战,所以统一使用json格式,前端通用 + # @param lineupID: 阵容ID + # @return: 阵容全部信息json字典,前端通用格式 + + playerID = curPlayer.GetPlayerID() + lineup = GetPlayerLineup(curPlayer, lineupID) + if not lineup.lineupHeroDict: + return {} + + 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, + } + + if not heroDict: + return {} + + lineupInfo = {"PlayerID":playerID, "FightPower":lineup.fightPower, "ShapeType":lineup.shapeType, "Hero":heroDict} + return lineupInfo + +def GetNPCLineupInfo(lineupID, strongerLV=0, difficulty=0): + ## 获取NPC阵容信息 + # @param lineupID: 阵容ID + # @param npcLV: 成长NPC等级 + # @param difficulty: 成长NPC难度系数 + # @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 + battleDict = GetNPCBattleDict(ipyData, npcID, strongerLV, difficulty) + if not battleDict: + continue + heroDict[str(posNum)] = battleDict + + lineupInfo = {"NPCLineupID":lineupID, "Hero":heroDict} + return lineupInfo + +def GetNPCBattleDict(lineupIpyData, npcID, strongerLV=0, difficulty=0): + ## 获取NPC战斗相关字典,支持成长NPC + # @param strongerLV: 成长等级 + # @param difficulty: 难度系数 + npcData = NPCCommon.GetNPCDataPy(npcID) + if not npcData: + return + heroID = npcData.GetRelatedHeroID() + npcLV = npcData.GetLV() + + lvIpyData = None + heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID) if heroID else None + npcStronger = IpyGameDataPY.GetIpyGameDataNotLog("NPCStronger", npcID) + if npcStronger and strongerLV: + lvIpyData = IpyGameDataPY.GetIpyGameData("PlayerLV", strongerLV) + if lvIpyData: + npcLV = strongerLV + if not lvIpyData: + lvIpyData = IpyGameDataPY.GetIpyGameData("PlayerLV", npcLV) + + if heroIpyData and lvIpyData: + skinIDList = heroIpyData.GetSkinIDList() + skinID = skinIDList[0] if skinIDList else 0 + skillIDList = GetNPCHeroSkillIDList(heroID, heroIpyData, lvIpyData.GetReHeroBreakLV(), lvIpyData.GetReHeroAwakeLV()) + else: + heroID = 0 + skinID = 0 + skillIDList = []# + npcData.GetSkillIDList() + + # boss额外随机技能 + bossID = lineupIpyData.GetBossID() + if npcID == bossID: + skillIDExList = lineupIpyData.GetSkillIDExList() + if skillIDExList: + randSkillIDExList = [] + list(skillIDExList) + skillExCnt = lineupIpyData.GetSkillExCnt() + if skillExCnt > 0 and len(randSkillIDExList) > skillExCnt: + random.shuffle(randSkillIDExList) + randSkillIDExList = randSkillIDExList[:skillExCnt] + skillIDList += randSkillIDExList + GameWorld.DebugLog("阵容boss技能: %s, 随机附加技能: %s" % (skillIDList, randSkillIDExList)) + + # 成长怪属性 + batAttrDict = GetNPCStrongerAttrDict(npcID, lvIpyData, npcStronger, difficulty) + if not batAttrDict: + 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(), + } + exAttrDict = npcData.GetSpecAttrInfo() + for attrIDStr, attrValue in exAttrDict.items(): + attrID = int(attrIDStr) + batAttrDict[attrID] = batAttrDict.get(attrID, 0) + attrValue + + battleDict = {"NPCID":npcID, + "HeroID":heroID, + "SkinID":skinID, + "LV":npcLV, + "AttrDict":{str(k):v for k, v in batAttrDict.items() if v > 0}, + "SkillIDList":skillIDList, + } + + GameWorld.DebugLog("GetNPCBattleDict npcID=%s,strongerLV=%s,difficulty=%s,%s" % (npcID, strongerLV, difficulty, battleDict)) + return battleDict + +def GetNPCHeroSkillIDList(heroID, heroIpyData, breakLV, awakeLV): + ## 获取NPC对应武将的技能ID列表 + normalSkillID = heroIpyData.GetNormalSkillID() + angerSkillID = heroIpyData.GetAngerSkillID() + skillIDList = [normalSkillID, angerSkillID] + + breakIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("HeroBreak", heroID) + if breakIpyDataList: + for breakIpyData in breakIpyDataList: + if breakIpyData.GetBreakLV() > breakLV: + break + skillID = breakIpyData.GetSkillID() + if skillID: + skillIDList.append(skillID) + + awakeIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("HeroAwake", heroID) + if awakeIpyDataList: + for awakeIpyData in awakeIpyDataList: + if awakeIpyData.GetAwakeLV() > awakeLV: + break + skillID = awakeIpyData.GetSkillID() + if skillID: + skillIDList.append(skillID) + + return skillIDList + +def GetNPCStrongerAttrDict(npcID, lvIpyData, npcStronger, difficulty): + ## 获取NPC成长属性 + # @param strongerLV: 成长等级 + # @param difficulty: 难度系数 + + batAttrDict = {} + if not lvIpyData or not npcStronger or not difficulty: + return batAttrDict + lv = lvIpyData.GetLV() + for attrID in ChConfig.CalcBattleAttrIDList: + attrIpyData = IpyGameDataPY.GetIpyGameData("PlayerAttr", attrID) + if not attrIpyData: + continue + attrName = attrIpyData.GetParameter() + if not hasattr(lvIpyData, "GetRe%s" % attrName): + continue + reValue = getattr(lvIpyData, "GetRe%s" % attrName)() # 基础参考值 + ratio = getattr(npcStronger, "Get%sRatio" % attrName)() if hasattr(npcStronger, "Get%sRatio" % attrName) else 1 # 属性系数 + attrValue = int(reValue * ratio * difficulty) + batAttrDict[attrID] = attrValue + #GameWorld.DebugLog(" attrID=%s,attrValue=%s,reValue=%s,ratio=%s,difficulty=%s" % (attrID, attrValue, reValue, ratio, difficulty)) + + GameWorld.DebugLog("NPC成长属性: npcID=%s,lv=%s,difficulty=%s,%s" % (npcID, lv, difficulty, batAttrDict)) + return batAttrDict + +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) + for posNumKey, heroInfo in heroDict.items(): + posNum = int(posNumKey) + + fightPower = 0 + skillIDList = [] # 战斗对象可能改变属性或技能,重新创建,防止误修改来源值 + attrDict = {} + skillIDList += heroInfo.get("SkillIDList", []) + attrDict.update(heroInfo.get("AttrDict", {})) + npcID = heroInfo.get("NPCID", 0) + heroID = heroInfo.get("HeroID", 0) + skinID = heroInfo.get("SkinID", 0) + lv = heroInfo.get("LV", 1) + specialty, atkDistType, country, sex = 0, 1, 0, 1 + heroIpyData = IpyGameDataPY.GetIpyGameData("Hero", heroID) if heroID else None + if heroIpyData: + objName = heroIpyData.GetName() + specialty = heroIpyData.GetSpecialty() + atkDistType = heroIpyData.GetAtkDistType() + country = heroIpyData.GetCountry() + sex = heroIpyData.GetSex() + + if lineupPlayerID: + fightPower = heroInfo.get("FightPower", 0) + if not heroIpyData: + continue + + else: + npcDataEx = NPCCommon.GetNPCDataPy(npcID) + if not npcDataEx: + continue + if not heroIpyData: + objName = npcDataEx.GetNPCName() + + batObj = batObjMgr.addBatObj() + if not batObj: + break + objID = batObj.GetID() + if npcID: + batObj.SetNPCID(npcID) + elif lineupPlayerID: + batObj.SetOwnerID(lineupPlayerID) + batObj.SetTFGUID(tfGUID) + batObj.SetName(objName) + batObj.SetFaction(faction) + batObj.SetLineupPos(posNum, num) + batObj.SetFightPower(fightPower) + batObj.SetLV(lv) + batObj.SetAtkDistType(atkDistType) + batObj.SetSpecialty(specialty) + batObj.SetCountry(country) + batObj.SetSex(sex) + batObj.SetHero(heroID, skinID) + + skillManager = batObj.GetSkillManager() + skillManager.SkillReset() + for skillID in skillIDList: + skillManager.LearnSkillByID(skillID) + + batLineup.posObjIDDict[posNum] = objID + GameWorld.DebugLog("AddBatObj %s,skill=%s" % (GetObjName(batObj), skillManager.GetSkillIDList())) + batObj.InitBatAttr({int(k):v for k, v in attrDict.items()}, initXP) + + return + +def ResetByNextTeam(turnFight): + ## 切换下一小队时重置相关,目前设置仅保留血量、怒气、已被击杀的不复活,其他重置 + + batFaction = turnFight.getBatFaction(ChConfig.Def_FactionA) + if not batFaction: + return + GameWorld.DebugLog("切换小队重置玩家阵容武将...") + batLineup = batFaction.getBatlineup(1) # 只处理玩家阵容 + batObjMgr = BattleObj.GetBatObjMgr() + for objID in batLineup.posObjIDDict.values(): + batObj = batObjMgr.getBatObj(objID) + if not batObj: + continue + objName = GetObjName(batObj) + if not batObj.IsAlive(): + GameWorld.DebugLog(" 已被击杀不处理! %s" % (objName)) + continue + GameWorld.DebugLog(" 重置武将: %s, HP:%s/%s, XP:%s" % (objName, batObj.GetHP(), batObj.GetMaxHP(), batObj.GetXP())) + + batObj.TurnReset() + + # 清除buff + buffMgr = batObj.GetBuffManager() + buffMgr.ClearBuff() + + # 重置CD + + # 重刷属性、被动 + TurnBuff.RefreshBuffAttr(batObj) + TurnPassive.RefreshPassive(batObj) + + return #// B4 10 回合制战斗 #tagCMTurnFight # #struct tagCMTurnFight #{ # tagHead Head; -# DWORD MapID; // 自定义地图ID,可用于绑定战斗场景功能(如野外关卡,爬塔功能,竞技场等) -# WORD FuncLineID; -# BYTE TagType; // 战斗目标类型,0-NPC,1-玩家,2-队伍 -# DWORD TagID; // 战斗目标类型对应的ID +# 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 @@ -54,149 +857,848 @@ tagID = clientData.TagID valueList = clientData.ValueList - playerID = curPlayer.GetPlayerID() - if tagType == ChConfig.TurnBattle_TagType_Player: - if tagID == playerID: - GameWorld.DebugLog("不能打自己!", playerID) - return - + 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 - # 需要发送到GameServer验证 - if mapID in ChConfig.Def_TFMapID_SendToGameServer: - SendToGameServer_TurnFight(curPlayer, "TurnFightRequest", [mapID, funcLineID, tagType, tagID, valueList]) + # 攻防方所使用的阵容ID + atkLineupID, defLineupID = FBLogic.GetFBPlayerLineupID(curPlayer, mapID, funcLineID) + if atkLineupID not in ShareDefine.LineupList or defLineupID not in ShareDefine.LineupList: return - DoTurnFightProcess(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick) - return - -def SendToGameServer_TurnFight(curPlayer, msgType, dataMsg=""): - 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: - return - - if msgType == "TurnFightRequest": - mapID, funcLineID, tagType, tagID, valueList = dataMsg - DoTurnFightProcess(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick) + # 玩家 + if tagType == 1: + OnTurnFightVSPlayer(curPlayer, mapID, funcLineID, atkLineupID, defLineupID, tagID) - 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) - - return - -def DoTurnFightProcess(curPlayer, mapID, funcLineID, tagType, tagID, valueList, tick): - ## 执行回合制战斗的完整流程 - - #if curPlayer.GetSightLevel() != curPlayer.GetID(): - # PlayerControl.SetPlayerSightLevel(curPlayer, curPlayer.GetID()) - - SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_Start) - - tagPlayerInfo = {} - if tagType == ChConfig.TurnBattle_TagType_Player and tagID >= 10000 : - tagPlayer = GameWorld.GetMapCopyPlayerManager().FindPlayerByID(tagID) - if tagPlayer: - tagPlayerInfo = __GetPlayerInfo(tagPlayer) - else: - SendToGameServer_TurnFight(curPlayer, "TurnFightTagPlayerInfo", [mapID, funcLineID, tagType, tagID, valueList]) + # NPC + else: + ret = FBLogic.GetFBNPCLineupInfo(curPlayer, mapID, funcLineID) + if not ret: return + npcLineupIDList, strongerLV, difficulty = ret + OnTurnFightVSNPC(curPlayer, mapID, funcLineID, atkLineupID, npcLineupIDList, strongerLV, difficulty) - 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: - return - if not tagInfo: - tagInfo = {} +def OnTurnFightVSNPC(curPlayer, mapID, funcLineID, atkLineupID, npcLineupIDList, strongerLV, difficulty): playerID = curPlayer.GetPlayerID() - GameWorld.DebugLog("回合战斗: mapID=%s,funcLineID=%s,tagType=%s,tagID=%s,valueList=%s,tagInfo=%s" - % (mapID, funcLineID, tagType, tagID, valueList, tagInfo), playerID) + 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 - factionSyncInfoA = __GetPlayerInfo(curPlayer) - factionSyncInfoB = tagInfo - SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, FightState_PrepareOK, msg=[factionSyncInfoA, factionSyncInfoB]) + lineupMainInfo = GetPlayerLineupInfo(curPlayer, atkLineupID) + if not lineupMainInfo: + GameWorld.DebugLog("玩家没有该阵容数据! atkLineupID=%s" % atkLineupID, playerID) + return - turnNum, turnMax = 1, 15 + turnMax = GetTurnMax(mapID) + if mapID == ChConfig.Def_FBMapID_MainBoss: + # 停止主线小怪战斗、清空 + mainFightMgr = GetMainFightMgr(curPlayer) + mainTF = mainFightMgr.turnFight + if mainTF.isInFight(): + mainTF.exitFight() + + tfMgr = GetTurnFightMgr() + turnFight = tfMgr.addTurnFight(mapID, funcLineID, playerID) + guid = turnFight.guid - 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) + turnFight.setTurnFight(mapID, funcLineID, turnMax, True) + turnFight.setPVE(npcLineupIDList, strongerLV, difficulty) + turnFight.setFactionLineup(ChConfig.Def_FactionA, {1:lineupMainInfo}) - factionTotalHurtDict = {} - - playbackID = 0 # 战斗回放ID,可根据该ID查看回放 - - # 战斗结束后处理 - 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 + for index, lineupID in enumerate(npcLineupIDList): + turnFight.lineupIndex = index + GameWorld.DebugLog("对战NPC阵容: index=%s, lineupID=%s" % (index, lineupID)) + if index > 0: + turnFight.nextTurnFight(resetByNextTeam=True) + turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(lineupID, strongerLV, difficulty)}) + turnFight.sortActionQueue() + turnFight.startFight() - if needSendGameServer or mapID in ChConfig.Def_TFMapID_SendToGameServer: - SendToGameServer_TurnFight(curPlayer, "TurnFightOver", [mapID, funcLineID, tagType, tagID, valueList, fightRet, awardItemList]) + __processTurnFight(turnFight.guid) - 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 not turnFight.isWin: + break + + PlayerOnline.GetOnlinePlayer(curPlayer).SetLastBatBuffer(guid, turnFight.batBuffer) + SyncTurnFightReport(curPlayer, guid, turnFight.batBuffer) + tfMgr.delTurnFight(guid) return -def SyncTurnFightState(curPlayer, mapID, funcLineID, tagType, tagID, state, turnNum=0, turnMax=0, msg=""): - if not curPlayer: +def OnTurnFightVSPlayer(curPlayer, mapID, funcLineID, atkLineupID, defLineupID, tagPlayerID): + playerID = curPlayer.GetPlayerID() + GameWorld.DebugLog("OnTurnFightVSPlayer: mapID=%s,funcLineID=%s,atkLineupID=%s,defLineupID=%s,tagPlayerID=%s" + % (mapID, funcLineID, atkLineupID, defLineupID, tagPlayerID), playerID) + atkLineupInfo = GetPlayerLineupInfo(curPlayer, atkLineupID) + if not atkLineupInfo: + GameWorld.DebugLog("玩家没有该阵容数据! atkLineupID=%s" % atkLineupID, playerID) 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) + + tagViewCache = PlayerViewCache.FindViewCache(tagPlayerID) + if not tagViewCache: + GameWorld.DebugLog("目标玩家没有缓存数据! tagPlayerID=%s" % tagPlayerID, playerID) + return {} + defLineupInfo = GetCacheLineupInfo(tagViewCache, defLineupID) + if not defLineupInfo: + GameWorld.DebugLog("目标玩家没有该阵容数据! tagPlayerID=%s,defLineupID=%s" % (tagPlayerID, defLineupID), playerID) + return + + turnMax = GetTurnMax(mapID) + + tfMgr = GetTurnFightMgr() + turnFight = tfMgr.addTurnFight(mapID, funcLineID, playerID) + guid = turnFight.guid + + turnFight.setTurnFight(mapID, funcLineID, turnMax, True) + turnFight.setPVP(tagPlayerID, tagViewCache) + turnFight.setFactionLineup(ChConfig.Def_FactionA, {1:atkLineupInfo}) + turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:defLineupInfo}) + turnFight.sortActionQueue() + turnFight.startFight() + + __processTurnFight(turnFight.guid) + + PlayerOnline.GetOnlinePlayer(curPlayer).SetLastBatBuffer(guid, turnFight.batBuffer) + SyncTurnFightReport(curPlayer, guid, turnFight.batBuffer) + tfMgr.delTurnFight(guid) + return + +def GetTurnMax(mapID): + if mapID == ChConfig.Def_FBMapID_Main: + return IpyGameDataPY.GetFuncCfg("TurnMax", 1) + mapTurnMaxDict= IpyGameDataPY.GetFuncEvalCfg("TurnMax", 3, {}) + return mapTurnMaxDict.get(mapID, IpyGameDataPY.GetFuncCfg("TurnMax", 2)) + +#// B4 13 主线战斗请求 #tagCSMainFightReq +# +#struct tagCSMainFightReq +#{ +# tagHead Head; +# BYTE ReqType; // 0-停止战斗回城;1-设置消耗倍值;2-挑战关卡小怪;4-继续战斗; +# DWORD ReqValue; // 请求值,ReqType为1时发送消耗倍值 +#}; +def OnMainFightReq(index, clientData, tick): + curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index) + reqType = clientData.ReqType + reqValue = clientData.ReqValue + + if reqType == 0: + __doExitMainFight(curPlayer) + return + elif reqType == 1: + __doSetFightPoint(curPlayer, reqValue) + return + + GameWorld.DebugLog("主线战斗请求: reqType=%s" % reqType, curPlayer.GetPlayerID()) + clientPack = ChPyNetSendPack.tagSCTurnFightReportSign() + clientPack.Sign = 0 + NetPackCommon.SendFakePack(curPlayer, clientPack) # 标记开始 + + if reqType == 2: # 前端主动请求开始关卡小怪的视为从休息中开始 + __doMainLevelWave(curPlayer, True) + 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(True) + 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() + 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 + + lineupIDList = getattr(levelIpyData, "GetWaveLineupIDList%s" % wave)() # 小队1阵容ID|小队2阵容ID|... + if not lineupIDList: + return + lineupID = lineupIDList[0] # NPC阵容ID + + lineupMainInfo = GetPlayerLineupInfo(curPlayer, ShareDefine.Lineup_Main) + if not lineupMainInfo: + GameWorld.DebugLog("没有设置主阵容!", playerID) + return + + strongerLV = levelIpyData.GetNPCLV() + difficulty = levelIpyData.GetDifficulty() + mainFightMgr = GetMainFightMgr(curPlayer) + mainFightMgr.chapterID = chapterID + mainFightMgr.levelNum = levelNum + mainFightMgr.waveMax = waveMax + mainFightMgr.wave = wave + mapID, funcLineID = ChConfig.Def_FBMapID_Main, PlayerControl.ComMainLevelValue(chapterID, levelNum, wave) + turnMax = GetTurnMax(mapID) + 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.setTurnFight(mapID, funcLineID, turnMax, False) + turnFight.setPVE(lineupIDList, strongerLV, 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 __doMainFight(curPlayer, tick=0): + '''执行主线战斗,单场战斗断电式战斗,以前端玩家手动点击节点做为断点处 + 每场战斗的初始化、结束默认断点,由前端决定自动继续或者点击继续 + ''' + + # 限制请求CD + if tick: + key = "MainFightReqTick" + lastTick = curPlayer.GetDictByKey(key) + if lastTick and tick - lastTick <= 1000: + GameWorld.DebugLog("主线战斗请求CD中") + return + curPlayer.SetDict(key, tick) + + mainFightMgr = GetMainFightMgr(curPlayer) + turnFight = mainFightMgr.turnFight + + isLevelBoss = mainFightMgr.isLevelBoss() + if isLevelBoss: + ## 关卡boss是一次性处理完的,一般不可能走到这里,这边做下防范 + return + + if not turnFight.isInFight(): + __doMainLevelWave(curPlayer, True) + return + + winFaction = turnFight.winFaction + if winFaction: + nextLineupID = turnFight.nextLineupID() + if nextLineupID: + GameWorld.DebugLog("---开始进入下一小队: lineupIndex=%s,nextLineupID=%s,%s" % (turnFight.lineupIndex, nextLineupID, turnFight.lineupIDList)) + + turnFight.nextTurnFight(resetByNextTeam=True) + # 切换小队时,玩家阵容不需要处理,保留状态 + turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(nextLineupID, turnFight.strongerLV, turnFight.difficulty)}) + turnFight.sortActionQueue() + turnFight.startFight() + + __doMainFight(curPlayer) + else: + __doMainLevelWave(curPlayer, False) + return + + # 小怪战斗,每次消耗1个战锤 + fightPoint = max(curPlayer.GetFightPoint(), 1) # 主线战斗消耗倍值,默认1 + if not PlayerControl.HaveMoney(curPlayer, ShareDefine.TYPE_Price_Xiantao, fightPoint): + GameWorld.DebugLog("回合开始时战锤不足!") + return + + # 以下均是处理关卡小怪分段实时战斗 + isEntry = EntryLogic(turnFight) + + # 是否开始检查断点,预判可断的点,方便前端点击体验,点下去就是玩家放的某个行动 + # 初始开始进场后,默认开始断点 + checkBreakpoint = True if isEntry else False + + batObjMgr = BattleObj.GetBatObjMgr() + turnNum = turnFight.turnNum + turnMax = turnFight.turnMax + for turnNum in range(turnNum, turnMax + 1): + turnTimeline = turnFight.getTurnNumStartTimelin(turnNum) # 本回合起始时间节点 + curTimeline = turnFight.getTimeline() + + # 回合开始 + turnTimeline += 1 # 每回合开始算一个时间节点 + if curTimeline < turnTimeline: + curTimeline = turnFight.setTimeline(turnTimeline) + GameWorld.DebugLog("【----- 回合制战斗轮次: %s -----】 curTimeline=%s" % (turnNum, curTimeline)) + turnFight.turnNum = 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) + TurnFightPerTurnBigStart(turnFight, batObj, turnNum) + + # 红颜 + # 灵兽 + + 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): + # 每个武将位算一个时间节点 + #GameWorld.DebugLog("武将节点: faction=%s,posNum=%s,curTimeline=%s" % (faction, posNum, curTimeline)) + if posNum not in batLineup.posObjIDDict: + batLineup.actionNum = posNum + 1 + curTimeline = turnFight.setTimeline(curTimeline + 1, True) + #GameWorld.DebugLog("空位节点: faction=%s,posNum=%s,curTimeline=%s" % (faction, posNum, curTimeline)) + continue + + objID = batLineup.posObjIDDict[posNum] + batObj = batObjMgr.getBatObj(objID) + + # 玩家自己阵营,预判可否行动 + if checkBreakpoint and faction == ChConfig.Def_FactionA and batObj: + if batObj.CanAction(): + GameWorld.DebugLog("玩家阵容下一个可行动的武将,断点: curTimeline=%s,nextPosNum=%s" % (curTimeline, posNum)) + return + + batLineup.actionNum = posNum + 1 + curTimeline = turnFight.setTimeline(curTimeline + 1) + TurnFightHeroTurnStart(turnFight, batObj, turnNum) + if not OnObjAction(turnFight, batObj): + continue + + if not checkBreakpoint and faction == ChConfig.Def_FactionA: + checkBreakpoint = True + + break + + if turnFight.actionIndex >= len(turnFight.actionSortList) - 1: + turnFight.actionIndex = 0 + else: + turnFight.actionIndex += 1 + + # 回合结束 + curTimeline = turnFight.setTimeline(curTimeline + 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(): + batObj = batObjMgr.getBatObj(objID) + TurnFightPerTurnBigEnd(turnFight, batObj, turnNum) + + if turnFight.checkOverByKilled(): + break + + if not turnFight.winFaction: + OnTurnAllOver(turnFight.guid) + + return + +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) + curTimeline = turnFight.getTurnNumStartTimelin(turnNum) # 本回合起始时间节点 + curTimeline = turnFight.setTimeline(curTimeline + 1) + 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) + TurnFightPerTurnBigStart(turnFight, batObj, turnNum) + + + # 红颜 + # 灵兽 + + 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: + curTimeline = turnFight.setTimeline(curTimeline + 1, True) + continue + curTimeline = turnFight.setTimeline(curTimeline + 1) # 每个武将位算一个时间节点 + objID = batLineup.posObjIDDict[posNum] + batObj = batObjMgr.getBatObj(objID) + TurnFightHeroTurnStart(turnFight, batObj, turnNum) + if not OnObjAction(turnFight, batObj): + continue + + break + + if turnFight.actionIndex >= len(turnFight.actionSortList) - 1: + turnFight.actionIndex = 0 + else: + turnFight.actionIndex += 1 + + # 回合结束 + curTimeline = turnFight.setTimeline(curTimeline + 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(): + batObj = batObjMgr.getBatObj(objID) + TurnFightPerTurnBigEnd(turnFight, batObj, turnNum) + + 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 + npcID = batObj.npcID + objName = GameWorld.CodeToGbk(batObj.GetName()) + if heroID: + objName += " Hero:%s" % heroID + if npcID: + objName += " NPC:%s" % npcID + return "%s%s-P%s ID:%s %s" % ("A" if faction == ChConfig.Def_FactionA else "B", num, posNum, batObj.GetID(), objName) + +def EntryLogic(turnFight): + ## 执行进场逻辑 + if turnFight.enterLogic: + return + GameWorld.DebugLog("执行进场逻辑...") + + batObjMgr = BattleObj.GetBatObjMgr() + 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) + turnFight.ResetOneActionUseSkillCnt() + TurnPassive.OnTriggerPassiveEffect(turnFight, batObj, ChConfig.TriggerWay_FightStart) + + turnFight.enterLogic = True + return True + +def OnTimelineChange(turnFight): + ## 每个时间节点变化时处理 + + nowTimeline = turnFight.getTimeline() + + batObjMgr = BattleObj.GetBatObjMgr() + for batFaction in turnFight.factionDict.values(): + for batLineup in batFaction.lineupDict.values(): + for objID in batLineup.posObjIDDict.values(): + batObj = batObjMgr.getBatObj(objID) + #GameWorld.DebugLog("OnTimelineChange! objID=%s" % (objID)) + if not batObj or not batObj.IsAlive(): + continue + + curID = batObj.GetID() + skillManager = batObj.GetSkillManager() + for index in range(0, skillManager.GetSkillCount()): + curSkill = skillManager.GetSkillByIndex(index) + if not curSkill: + continue + remainTime = curSkill.GetRemainTime() + if not remainTime: + continue + calcTimeline = curSkill.GetCalcTime() + passTurn = __calcPassturn(calcTimeline, nowTimeline, True) + if passTurn <= 0: + continue + skillID = curSkill.GetSkillID() + updRemainTime = max(0, remainTime - passTurn) + curSkill.SetRemainTime(updRemainTime) + curSkill.SetCalcTime(nowTimeline) + GameWorld.DebugLog("更新技能剩余回合数: curID=%s,skillID=%s,updRemainTime=%s,calcTimeline=%s,passTurn=%s" + % (curID, skillID, updRemainTime, calcTimeline, passTurn)) + + 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.GetSkillType() in ChConfig.Def_LstBuff_List: + #GameWorld.DebugLog(" 持续类buff由触发时机决定剩余时间! curID=%s,index=%s,skillID=%s,buffID=%s" % (curID, index, skillID, buffID)) + continue + remainTime = buff.GetRemainTime() + if not remainTime: + # 永久buff不处理 + #GameWorld.DebugLog(" 永久buff不处理! curID=%s,index=%s,skillID=%s" % (curID, index, skillID)) + continue + calcTimeline = buff.GetCalcTime() + passTurn = __calcPassturn(calcTimeline, nowTimeline, False) + if passTurn <= 0: + #GameWorld.DebugLog(" passTurn <= 0 passTurn=%s,calcTimeline=%s,nowTimeline=%s,skillID=%s" % (passTurn, calcTimeline, nowTimeline, skillID)) + continue + + updRemainTime = max(0, remainTime - passTurn) + GameWorld.DebugLog("更新buff剩余回合数: curID=%s,buffID=%s,skillID=%s,updRemainTime=%s,calcTimeline=%s,passTurn=%s" + % (curID, buffID, skillID, updRemainTime, calcTimeline, passTurn)) + if updRemainTime > 0: + buff.SetRemainTime(updRemainTime) + buff.SetCalcTime(nowTimeline) + TurnBuff.SyncBuffRefresh(turnFight, batObj, buff) + else: + TurnBuff.DoBuffDel(turnFight, batObj, buff) + + return + +def __calcPassturn(calcTimeline, nowTimeline, equalOK): + ## 计算已经过了的回合数 + # @param equalOK: 时间节点相同时是否算1回合,一般技能可以算,buff不算 + calcTurnNum = calcTimeline / TimelineSet + calcTimeNode = calcTimeline % TimelineSet + nowTurnNum = nowTimeline / TimelineSet + nowTimeNode = nowTimeline % TimelineSet + if equalOK: + if nowTimeNode >= calcTimeNode: + return max(0, nowTurnNum - calcTurnNum) + return max(0, nowTurnNum - calcTurnNum - 1) + else: + if nowTimeNode > calcTimeNode: + return max(0, nowTurnNum - calcTurnNum) + return max(0, nowTurnNum - calcTurnNum - 1) + return 0 + +def TurnFightPerTurnBigStart(turnFight, batObj, turnNum): + ## 大回合开始时 + if not batObj: + return + + if batObj.GetHP() <= 0: + return + + turnFight.ResetOneActionUseSkillCnt() + TurnPassive.OnTriggerPassiveEffect(turnFight, batObj, ChConfig.TriggerWay_BigTurnStart) + return + +def TurnFightPerTurnBigEnd(turnFight, batObj, turnNum): + ## 大回合结束时 + if not batObj: + return + + if batObj.GetHP() <= 0: + return + + TurnPassive.OnTriggerPassiveEffect(turnFight, batObj, ChConfig.TriggerWay_BigTurnEnd) + return + +def TurnFightHeroTurnStart(turnFight, batObj, turnNum): + ## 武将回合开始时,不能行动也要触发 + if not batObj: + return + + if batObj.GetHP() <= 0: + return + + GameWorld.DebugLog("---[武将回合开始时] : curID=%s,curHP=%s/%s" % (batObj.GetID(), batObj.GetHP(), batObj.GetMaxHP())) + turnFight.ResetOneActionUseSkillCnt() + TurnPassive.OnTriggerPassiveEffect(turnFight, batObj, ChConfig.TriggerWay_HeroTurnStart) + 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, skillID=0, isBounce=False): + ## 回合对象添加伤害值 + # @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)) + + 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)) + + 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 = curBatObj.CanAction() + 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) + + TurnPassive.OnTriggerPassiveEffect(turnFight, curBatObj, ChConfig.TriggerWay_HeroActionStart) + + 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() not in [ChConfig.Def_SkillFuncType_TurnNormaSkill, ChConfig.Def_SkillFuncType_AngerSkill]: + #只能主动释放普攻或怒技 + continue + #被动技能无法使用 + if SkillCommon.isPassiveSkill(useSkill): + continue + #还在冷却时间内无法释放 + if useSkill.GetRemainTime(): + continue + skillID = useSkill.GetSkillID() + # 常规攻击优先xp + if SkillCommon.isAngerSkill(useSkill): + if curXP < xpMax: + continue + if curBatObj.CheckInState(ChConfig.BatObjState_Sneer): + GameWorld.DebugLog("嘲讽状态下,无法主动释放怒技!") # 可被动释放怒技,如怒技追击 + 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): + break + + TurnPassive.OnTriggerPassiveEffect(turnFight, curBatObj, ChConfig.TriggerWay_HeroActionEnd) + return True + +def SetObjKilled(turnFight, gameObj, killer=None, useSkill=None): + objID = gameObj.GetID() + killerObjID = killer.GetID() if killer else 0 + skillID = useSkill.GetSkillID() if useSkill else 0 + GameWorld.DebugLog(" %s 回合战斗主体被击杀: curID=%s,killerObjID=%s,skillID=%s" % (GetObjName(gameObj), objID, killerObjID, skillID)) + gameObj.SetDead() + + clientPack = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagMCTurnFightObjDead) + clientPack.ObjID = objID + clientPack.KillerObjID = killerObjID + clientPack.SkillID = skillID + turnFight.addBatPack(clientPack) + + curPlayer = turnFight.curPlayer + # 暂时只算主线小怪 + if curPlayer and turnFight.mapID == ChConfig.Def_FBMapID_Main and gameObj.GetFaction() != ChConfig.Def_FactionA: + PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_KillNPC, 1) + 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 + turnFight.isWin = (winFaction == ChConfig.Def_FactionA) + GameWorld.DebugLog("--- 战斗结束处理 ---, winFaction=%s, costTime=%ss, %s" % (winFaction, turnFight.costTime, guid)) + + # 统计明细 + 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,npcID=%s,heroID=%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} + + 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) + return + +#// B4 14 查看战报 #tagCSTurnFightReportView +# +#struct tagCSTurnFightReportView +#{ +# tagHead Head; +# char GUID[40]; //战报guid +#}; +def OnTurnFightReportView(index, clientData, tick): + curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index) + guid = clientData.GUID + + lastBatBufferInfo = PlayerOnline.GetOnlinePlayer(curPlayer).GetLastBatBuffer() + if lastBatBufferInfo and len(lastBatBufferInfo) == 2 and guid == lastBatBufferInfo[0]: + guid, reprot = lastBatBufferInfo + SyncTurnFightReport(curPlayer, guid, reprot) + return + + # 其他战报,一般是入库存储的,待扩展 + + # 战报已过期 + PlayerControl.NotifyCode(curPlayer, "FightReportExpired") + 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 + -- Gitblit v1.8.0