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 | 236 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 185 insertions(+), 51 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 98b9907..111c080 100644 --- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py +++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py @@ -21,6 +21,7 @@ import ChConfig import PlayerTask +import PlayerViewCache import ChPyNetSendPack import NetPackCommon import PlayerControl @@ -35,6 +36,7 @@ import TurnPassive import TurnSkill import TurnBuff +import CommFunc import ObjPool import FBLogic @@ -160,29 +162,47 @@ self.timeline = 0 # 时间轴节点 turnNum*1000+actionIndex*100++actionNum self.startTime = 0 # 开始时间戳,支持毫秒小数 self.costTime = 0 # 单场战斗总耗时,支持毫秒小数 + self._oneActionUseSkillCntDict = {} # 某对象行动开始后所有对象累计使用技能次数,用于单对象单次行动中限制每个对象的最高触发技能次数 {objID:useCnt, ...} - # 多小队 - 一般只有PVE用到 + # 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={}, lineupIDList=[], strongerLV=0, difficulty=0): + def setTurnFight(self, mapID, funcLineID, turnMax, isNeedReport=False, msgDict={}): ## 设置本场回合战斗设定 self.mapID = mapID self.funcLineID = funcLineID self.turnMax = turnMax # 最大回合数 self.isNeedReport = isNeedReport - self.lineupIndex = 0 - self.lineupIDList = lineupIDList - self.strongerLV = strongerLV - self.difficulty = difficulty + self.setPVE() + self.setPVP() self.msgDict = {} self.nextTurnFight(msgDict) return - def nextTurnFight(self, msgDict={}): + 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 @@ -192,6 +212,8 @@ self.timeline = 0 self.startTime = time.time() self.costTime = 0 + if resetByNextTeam: + ResetByNextTeam(self) return def haveNextLineup(self): @@ -244,7 +266,7 @@ @param turnNum: 第x回合 ''' self.timeline = timeline - GameWorld.DebugLog("时间节点更新: %s" % self.timeline) + GameWorld.DebugLog("[时间节点更新]: %s" % self.timeline) if isEmpty: # 空位置的节点可直接跳过 return timeline @@ -393,7 +415,9 @@ headStr = "%02x%02x" % (clientPack.Cmd, clientPack.SubCmd) if self.isNeedReport: packBuff = clientPack.GetBuffer() - GameWorld.DebugLog("回合战斗过程封包: %s" % (headStr)) + buffLen = len(packBuff) + GameWorld.DebugLog("回合战斗过程封包: %s, len:%s" % (headStr, buffLen)) + self.batBuffer += CommFunc.WriteWORD("", buffLen) self.batBuffer += packBuff ObjPool.GetPoolMgr().release(clientPack) else: @@ -404,6 +428,12 @@ 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(): ## 回合战斗管理器 @@ -476,10 +506,30 @@ % (chapterID, levelNum, nowChapterID, fixNowValue), curPlayer.GetPlayerID()) return -def GetPlayerLineupInfoByCache(playerID, lineupID): - ## 获取玩家阵容信息 - 根据玩家查看缓存 - lineupInfo = {} + +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格式,前端通用 @@ -487,11 +537,10 @@ # @return: 阵容全部信息json字典,前端通用格式 playerID = curPlayer.GetPlayerID() - lineup = PlayerOnline.GetOnlinePlayer(curPlayer).GetLineup(lineupID) + lineup = GetPlayerLineup(curPlayer, lineupID) if not lineup.lineupHeroDict: return {} - lineupInfo = {"PlayerID":playerID, "FightPower":lineup.fightPower, "ShapeType":lineup.shapeType} heroDict = {} curPack = curPlayer.GetItemManager().GetPack(ShareDefine.rptHero) for posNum in lineup.lineupHeroDict.keys(): @@ -517,8 +566,11 @@ "AttrDict":{str(k):v for k, v in hero.heroBatAttrDict.items() if v > 0}, "SkillIDList":skillIDlist, } - lineupInfo.update({"Hero":heroDict}) + + if not heroDict: + return {} + lineupInfo = {"PlayerID":playerID, "FightPower":lineup.fightPower, "ShapeType":lineup.shapeType, "Hero":heroDict} return lineupInfo def GetNPCLineupInfo(lineupID, strongerLV=0, difficulty=0): @@ -573,7 +625,7 @@ else: heroID = 0 skinID = 0 - skillIDList = [] + npcData.GetSkillIDList() + skillIDList = []# + npcData.GetSkillIDList() # boss额外随机技能 bossID = lineupIpyData.GetBossID() @@ -600,7 +652,10 @@ ChConfig.AttrID_ParryRate:npcData.GetParryRate(), ChConfig.AttrID_ParryRateDef:npcData.GetParryRateDef(), ChConfig.AttrID_SuckHPPer:npcData.GetSuckHPPer(), ChConfig.AttrID_SuckHPPerDef:npcData.GetSuckHPPerDef(), } - batAttrDict.update(npcData.GetSpecAttrInfo()) + exAttrDict = npcData.GetSpecAttrInfo() + for attrIDStr, attrValue in exAttrDict.items(): + attrID = int(attrIDStr) + batAttrDict[attrID] = batAttrDict.get(attrID, 0) + attrValue battleDict = {"NPCID":npcID, "HeroID":heroID, @@ -619,7 +674,7 @@ angerSkillID = heroIpyData.GetAngerSkillID() skillIDList = [normalSkillID, angerSkillID] - breakIpyDataList = IpyGameDataPY.GetIpyGameDataList("HeroBreak", heroID) + breakIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("HeroBreak", heroID) if breakIpyDataList: for breakIpyData in breakIpyDataList: if breakIpyData.GetBreakLV() > breakLV: @@ -628,7 +683,7 @@ if skillID: skillIDList.append(skillID) - awakeIpyDataList = IpyGameDataPY.GetIpyGameDataList("HeroAwake", heroID) + awakeIpyDataList = IpyGameDataPY.GetIpyGameDataListNotLog("HeroAwake", heroID) if awakeIpyDataList: for awakeIpyData in awakeIpyDataList: if awakeIpyData.GetAwakeLV() > awakeLV: @@ -683,11 +738,9 @@ batObjMgr = BattleObj.GetBatObjMgr() initXP = IpyGameDataPY.GetFuncCfg("AngerXP", 1) - atkBackSkillIDList = IpyGameDataPY.GetFuncEvalCfg("ParryCfg", 2) for posNumKey, heroInfo in heroDict.items(): posNum = int(posNumKey) - atkBackSkillID = 0 # 反击技能ID fightPower = 0 skillIDList = [] # 战斗对象可能改变属性或技能,重新创建,防止误修改来源值 attrDict = {} @@ -697,10 +750,12 @@ 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: - atkDistType = heroIpyData.GetAtkDistType() objName = heroIpyData.GetName() + specialty = heroIpyData.GetSpecialty() + atkDistType = heroIpyData.GetAtkDistType() country = heroIpyData.GetCountry() sex = heroIpyData.GetSex() @@ -714,10 +769,7 @@ if not npcDataEx: continue if not heroIpyData: - atkDistType = npcDataEx.GetAtkDistType() objName = npcDataEx.GetNPCName() - country = npcDataEx.GetCountry() - sex = npcDataEx.GetSex() batObj = batObjMgr.addBatObj() if not batObj: @@ -734,16 +786,11 @@ batObj.SetFightPower(fightPower) batObj.SetLV(lv) batObj.SetAtkDistType(atkDistType) + batObj.SetSpecialty(specialty) batObj.SetCountry(country) batObj.SetSex(sex) batObj.SetHero(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: @@ -752,6 +799,39 @@ 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 @@ -788,12 +868,11 @@ atkLineupID, defLineupID = FBLogic.GetFBPlayerLineupID(curPlayer, mapID, funcLineID) if atkLineupID not in ShareDefine.LineupList or defLineupID not in ShareDefine.LineupList: return - + # 玩家 if tagType == 1: - # OnTurnFightVSPlayer - pass - + OnTurnFightVSPlayer(curPlayer, mapID, funcLineID, atkLineupID, defLineupID, tagID) + # NPC else: ret = FBLogic.GetFBNPCLineupInfo(curPlayer, mapID, funcLineID) @@ -816,9 +895,8 @@ GameWorld.DebugLog("玩家没有该阵容数据! atkLineupID=%s" % atkLineupID, playerID) return - turnMax = 15 + turnMax = GetTurnMax(mapID) if mapID == ChConfig.Def_FBMapID_MainBoss: - turnMax = IpyGameDataPY.GetFuncCfg("Mainline", 3) # 停止主线小怪战斗、清空 mainFightMgr = GetMainFightMgr(curPlayer) mainTF = mainFightMgr.turnFight @@ -829,14 +907,15 @@ turnFight = tfMgr.addTurnFight(mapID, funcLineID, playerID) guid = turnFight.guid - turnFight.setTurnFight(mapID, funcLineID, turnMax, True, lineupIDList=npcLineupIDList, strongerLV=strongerLV, difficulty=difficulty) + turnFight.setTurnFight(mapID, funcLineID, turnMax, True) + turnFight.setPVE(npcLineupIDList, strongerLV, difficulty) turnFight.setFactionLineup(ChConfig.Def_FactionA, {1:lineupMainInfo}) for index, lineupID in enumerate(npcLineupIDList): turnFight.lineupIndex = index GameWorld.DebugLog("对战NPC阵容: index=%s, lineupID=%s" % (index, lineupID)) if index > 0: - turnFight.nextTurnFight() + turnFight.nextTurnFight(resetByNextTeam=True) turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(lineupID, strongerLV, difficulty)}) turnFight.sortActionQueue() turnFight.startFight() @@ -846,9 +925,54 @@ if not turnFight.isWin: break + PlayerOnline.GetOnlinePlayer(curPlayer).SetLastBatBuffer(guid, turnFight.batBuffer) SyncTurnFightReport(curPlayer, guid, turnFight.batBuffer) tfMgr.delTurnFight(guid) return + +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 + + 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 # @@ -961,13 +1085,14 @@ mainFightMgr.levelNum = levelNum mainFightMgr.waveMax = waveMax mainFightMgr.wave = wave - turnMax = IpyGameDataPY.GetFuncCfg("Mainline", 2) 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, lineupIDList=lineupIDList, strongerLV=strongerLV, difficulty=difficulty) + 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() @@ -1008,7 +1133,7 @@ if nextLineupID: GameWorld.DebugLog("---开始进入下一小队: lineupIndex=%s,nextLineupID=%s,%s" % (turnFight.lineupIndex, nextLineupID, turnFight.lineupIDList)) - turnFight.nextTurnFight() + turnFight.nextTurnFight(resetByNextTeam=True) # 切换小队时,玩家阵容不需要处理,保留状态 turnFight.setFactionLineup(ChConfig.Def_FactionB, {1:GetNPCLineupInfo(nextLineupID, turnFight.strongerLV, turnFight.difficulty)}) turnFight.sortActionQueue() @@ -1049,7 +1174,7 @@ turnFight.syncState(FightState_Fighting) for faction, num in turnFight.actionSortList: - GameWorld.DebugLog("回合开始逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num)) + GameWorld.DebugLog("大回合开始逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num)) batFaction = turnFight.getBatFaction(faction) batLineup = batFaction.getBatlineup(num) batLineup.actionNum = 1 @@ -1140,7 +1265,7 @@ # 回合开始 for faction, num in turnFight.actionSortList: - GameWorld.DebugLog("回合开始逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num)) + GameWorld.DebugLog("大回合开始逻辑: turnNum=%s,faction=%s, num=%s" % (turnNum, faction, num)) batFaction = turnFight.getBatFaction(faction) batLineup = batFaction.getBatlineup(num) batLineup.actionNum = 1 @@ -1225,6 +1350,7 @@ 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 @@ -1243,10 +1369,6 @@ #GameWorld.DebugLog("OnTimelineChange! objID=%s" % (objID)) if not batObj or not batObj.IsAlive(): continue - - batObj.SetDict(ChConfig.Def_Obj_Dict_TurnComboNum, 0) - batObj.SetDict(ChConfig.Def_Obj_Dict_TurnMissNum, 0) - batObj.SetDict(ChConfig.Def_Obj_Dict_TurnParryNum, 0) curID = batObj.GetID() skillManager = batObj.GetSkillManager() @@ -1325,6 +1447,7 @@ if batObj.GetHP() <= 0: return + turnFight.ResetOneActionUseSkillCnt() TurnPassive.OnTriggerPassiveEffect(turnFight, batObj, ChConfig.TriggerWay_BigTurnStart) return @@ -1347,6 +1470,8 @@ 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 @@ -1437,7 +1562,7 @@ if SkillCommon.isAngerSkill(useSkill): if curXP < xpMax: continue - if curBatObj.IsInState(ChConfig.BatObjState_Sneer): + if curBatObj.CheckInState(ChConfig.BatObjState_Sneer): GameWorld.DebugLog("嘲讽状态下,无法主动释放怒技!") # 可被动释放怒技,如怒技追击 continue useCnt = -1 # xp技能优先释放 @@ -1553,10 +1678,19 @@ # 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 - #SyncTurnFightReport(curPlayer, guid, reprot) + # 其他战报,一般是入库存储的,待扩展 + + # 战报已过期 + PlayerControl.NotifyCode(curPlayer, "FightReportExpired") return def SyncTurnFightReport(curPlayer, guid, reprot): -- Gitblit v1.8.0