local EventSystem = require('Event/EventSystem') local Base = require("Battle/Logic/Level/LevelFunction/SyncLevelFunction") local SyncWorldBossLevelFunction = class("SyncWorldBossLevelFunction",Base) local WorldBossAutoTest = require("Tools/WorldBossAutoTest") local world_boss_pb = require('Proto/world_boss_pb') local FormulaCalculator = require"Battle/Logic/Missile/FormulaCalculator" local private = { RecordHpDic = {}, --value是FP DamageList = {},--排序过的伤害列表 CurFrameHurtMainPlayerHeroes = {}, --本帧对主角造成伤害的玩家列表 RecordReliveTimes = 0, -- 记录自己复活次数 RecordKillNumDic = {}, --记录玩家的总杀敌人(中途退场的也要记录) playerid - killNum } local Const_NotGuideRobotHeroNum = 6 -- 非助战和非引导场的机器人的武将数量 local Const_RobotReviveCheckFrame = 40 -- 每2秒检测一次机器人是否需要复活 local Const_RobotRandomActiveMaxFrame = 30 * 20 -- 机器人激活时间最大范围 30秒 local Const_KillerDeBuff = {100094, 100095} -- 给杀人者加上的debuff local Const_RebornTimesMax = -1 -- 最大复活次数 local Const_RebornAutoTime = {20} -- 每次的复活时间 local IgnoreBossBuffID = LuaConst.SpecialBuffs.IgnoreBoss --不可对boss造成伤害的buff local MY_CAMPID_WHEN_TUTORIAL = 1 local ENECAMPEAMID_WHEN_TUTORIAL = 2 local totalTime = nil local WorldBossKillConditionInFrame = DataSheetManager.ConstData:GetDataByID("WorldBossKillCondition") and DataSheetManager.ConstData:GetDataByID("WorldBossKillCondition").IntValue * LuaConst.LogicFrameRate or nil function SyncWorldBossLevelFunction:GetExpClass() return require "Battle/Expression/ExpLevel/ExpLevelFunction/ExpSyncWorldBossLevelFunction" end function SyncWorldBossLevelFunction:InitLevel() Base.InitLevel(self) self.isFreeToEnterAndLeave = true --如果是随进随出,必须在Base.InitLevel之后把这个设为true self.LevelReviveType = LuaConst.LevelReviveType.SelectOne -- 复活:一次选一主将活 self.isSwitchOneReviveNeerBy = true -- 复活的地点为 出生点 。 self.allPlayerHeroDeadCount = 0 --所有玩家的英雄阵亡的次数累计(1个主将死亡加一次,用作计算第一滴血) self.ShowTeammateIcon = false self.canHurtBossDic = {} --key 是playerID,value是bool值,指定是否可以对boss造成伤害 private.RecordHpDic = {} private.DamageList = {} private.RecordReliveTimes = 0 private.PlayerID2ForceTeamIdDic = {} --记录了每个Player的宣战目标队伍ID --※如果有组队,队伍ID为一个string,如果没有入队,那么队伍ID是playerID,是number private.RecordKillNumDic = {} private.RecordComboKillNumDic = {} self.KillRecordList = {} --击杀信息列表,元素是一个结构,{killerName=击杀者name, killerAcid=击杀者acid, deadName=死者名字, deadAcid=死者acid, times=击杀次数, firstFrameNum=第一次击杀的帧号, lastFrameNum=最后一次击杀的帧号} totalTime = FP.tonumberV1(LevelManager.LevelTotalTime()) * LuaConst.LogicFrameRate self.tutorialAssistRobotPlayerIDs = nil self.robotRandomActiveDic = {} -- 模拟玩家的机器人( 非引导机器人且非助战机器人)的激活时间 key = frame , value = {playerID,...} self.waitReviewRobotList = {} -- 需要每秒检测一下复活武将的机器人,存储的是playerID end function SyncWorldBossLevelFunction:InitLevelDifficulty() local levelID = LevelManager.GetCurLevelID() for _,data in DataSheetManager.WorldBoss:GetEnumerator() do if data.MissonDataID == levelID then self.LevelGs = data.Battle self.SubjugateDataPVEID = data.SubjugateDataPVE break end end end function SyncWorldBossLevelFunction:GetBornIndex(playerInfo) if playerInfo.fighterType == LuaConst.BattleMemberType.Robot_Hero then return (playerInfo.playerID - 1) % LevelManager.bornPointCount + 1 else local teamIdNum = playerInfo.teamId local teamIdType = type(teamIdNum) if teamIdType == "string" then teamIdNum = teamIdNum:byte() else if teamIdType ~= "number" then logging.error(LTAG.ERROR, "HYW : teamId type Error! type : ", teamIdType) teamIdNum = 1 end end return ((teamIdNum - 1) % LevelManager.bornPointCount) + 1 end end function SyncWorldBossLevelFunction:CreateRobot(playerID) local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerID) if playerInfo.fighterType == LuaConst.BattleMemberType.Robot_Hero and playerInfo.campID == MY_CAMPID_WHEN_TUTORIAL then playerInfo.assisAimAcid = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(LevelPlayerManager.MyPlayerIDInRoom).acid -- 主视角safe self.tutorialAssistRobotPlayerIDs = self.tutorialAssistRobotPlayerIDs or {} --引导关我方助战机器人不在此创建 self.tutorialAssistRobotPlayerIDs[#self.tutorialAssistRobotPlayerIDs + 1] = playerID else if playerInfo.fighterType == LuaConst.BattleMemberType.Robot_Hero and (not playerInfo.isGuideRobot) then -- 非引导机器人,需要在 10秒内陆续创建出来 local activeMaxFrame = Const_RobotRandomActiveFrame if playerInfo.wbId > 0 then activeMaxFrame = DataSheetManager.WorldBoss:GetDataByID(playerInfo.wbId).CreateAITime * LuaConst.LogicFrameRate end local activeFrame = LogicRandom.GetIntegerValue(nil, 1, activeMaxFrame, 20240605) if self.robotRandomActiveDic[activeFrame] == nil then self.robotRandomActiveDic[activeFrame] = {playerID} else table.insert(self.robotRandomActiveDic[activeFrame], playerID) end else --不调用Base self:SetPlayerActive(playerID) end end end function SyncWorldBossLevelFunction:InitPlayerStatistic(statistic) Base.InitPlayerStatistic(self, statistic) statistic[LuaConst.EStatisticType.PlayerBeKilledByBossCount] = 0 end function SyncWorldBossLevelFunction:TryActiveTutorialAssistRobot() if self.tutorialAssistRobotPlayerIDs then for i,playerID in ipairs(self.tutorialAssistRobotPlayerIDs) do self:SetPlayerActive(playerID) end self.tutorialAssistRobotPlayerIDs = nil end end function SyncWorldBossLevelFunction:HasTutorialAssitRobot() return self.tutorialAssistRobotPlayerIDs and #self.tutorialAssistRobotPlayerIDs > 0 end function SyncWorldBossLevelFunction:IsAssitant(playerInfo) return playerInfo.fighterType == LuaConst.BattleMemberType.Assistant or (playerInfo.fighterType == LuaConst.BattleMemberType.Robot_Hero and playerInfo.campID == MY_CAMPID_WHEN_TUTORIAL) --我方机器人认为是助战者(这里代码很怪,因为枚举定义有问题,机器人和助战者是两个枚举,然而可能存在机器人助战者,所以只能这么瞎写了) end function SyncWorldBossLevelFunction:CreatePlayerEntities(playerIDInRoom ,playerHeroArmy) --不调用Base local heroEntityTable = {} local army = playerHeroArmy.army local playerInfo = playerHeroArmy.playerInfo local worldBossData if playerInfo.wbId > 0 then worldBossData = DataSheetManager.WorldBoss:GetDataByID(playerInfo.wbId) end self.exp:OnCreatePlayerEntitys(worldBossData) local isPlayerSelf = playerInfo.playerID == LevelPlayerManager.MyPlayerIDInRoom local totalGS = 0 for _,heroArmyData in ipairs(army) do totalGS = totalGS + heroArmyData.heroGs end for i,heroArmyData in ipairs(army) do local entity = self:CreatePlayerEntity(playerInfo,heroArmyData,isPlayerSelf) entity:SetGs(totalGS) table.insert(heroEntityTable,entity) end LevelPlayerManager.SetPlayerEntityTable(heroEntityTable ,playerIDInRoom) private.RecordKillNumDic[playerInfo.playerID] = 0 private.RecordComboKillNumDic[playerInfo.playerID] = 0 return heroEntityTable end function SyncWorldBossLevelFunction:CreatePlayerEntity(playerInfo, heroArmyData,isPlayerSelf) local entity = nil if playerInfo.fighterType == LuaConst.BattleMemberType.Robot_Hero and (not playerInfo.isGuideRobot) then entity = self:CreateRobotPlayerEntity(playerInfo, heroArmyData, self:GetBornIndex(playerInfo)) else entity = Base.CreatePlayerEntity(self, playerInfo, heroArmyData, isPlayerSelf, self:GetBornIndex(playerInfo)) end -- 测试一致性的设置 测试代码↓ if entity._logicInitData.BattleMemberType == LuaConst.BattleMemberType.AI_Hero then entity.components.autoFight.IsAIAutoTest = true SystemManager.SetAIByID(entity , LuaConst.WorldBossTestAIID) end -- 测试代码↑ --new↓ 封了个方法 if self:IsAssitant(playerInfo) then entity:SetTeamID(nil)--世界boss模式中,如果单位是协助者,则认为他没有队伍,强行设置TeamID为nil。为的是让请求队友助战时逻辑能正常 end --new↑ return entity end -- 创建 服务器模拟的玩家机器人(非助战和非引导场的机器人) - 此方法基本是抄写的CreatePlayerEntity function SyncWorldBossLevelFunction:CreateRobotPlayerEntity(playerInfo, heroArmyData, index) local bornItem = self:GetBornItem(playerInfo, index) -- new↓ 支持 固定/范围随机 位置出生 local pos = nil if playerInfo.Pos then pos = playerInfo.Pos else pos = LogicRandom.GetRandomPointInLevelItem(nil, bornItem.ItemData, 8238239) end pos = pos:cloneV2() local dir = playerInfo.Dir or bornItem.ItemData.ItemDir local attr = self:GetRobotAttribute(playerInfo.roleGsSum, heroArmyData.HeroID, heroArmyData.AdjutantID) EntityFactory.SetParams("BornItemID", bornItem.LevelItemID, "CampID", playerInfo.campID, "Name", playerInfo.name) EntityFactory.SetType_ID(LuaConst.CharacterType.Hero, heroArmyData.HeroID) EntityFactory.SetParams("StratagemSkill", playerInfo.stratagemSkill) EntityFactory.SetAttrTable_ID(attr) EntityFactory.SetPos_Dir(pos, dir) EntityFactory.SetSelf_Sync(false, true) EntityFactory.SetEffectBreak_Weapon(heroArmyData.mainHeroBreakNums, heroArmyData.isMagicWeaponActive,heroArmyData.mainHeroWeaponLevels) EntityFactory.SetHorse_ID(heroArmyData.horseID, heroArmyData.horseAppearanceId) if heroArmyData.petInfo then EntityFactory.SetPet_Info(heroArmyData.petInfo.petId, heroArmyData.petInfo.stage, heroArmyData.petInfo.petName, heroArmyData.petInfo.petSkill) EntityFactory.SetPet_TitleInfo(heroArmyData.petInfo.isLineUp, heroArmyData.petInfo.runeSkillLevelSum, heroArmyData.petInfo.aptitudeSum) end EntityFactory.SetHeroAppearance_ID(heroArmyData.heroAppearanceId) EntityFactory.SetWeaponAppearance_ID(heroArmyData.weaponAppearanceId) EntityFactory.SetLightEffect_ID(heroArmyData.lightEffectIds) EntityFactory.SetWeaponSoulInfo(heroArmyData.weaponSoulInfo) EntityFactory.SetParams("PlayerID", playerInfo.playerID) local entity = EntityFactory.DoCreatePlayerEntity() entity._logicInitData.BattleMemberType = playerInfo.fighterType entity._logicInitData.elo = playerInfo.elo --entity._logicInitData.PlayerIDInRoom = playerInfo.playerID entity._logicInitData.TeamID = playerInfo.teamId entity._logicInitData.GuildID = playerInfo.guildID entity._logicInitData.TeamLeaderName = playerInfo.teamLeaderName entity._logicInitData.OfficialPosition = playerInfo.officialPosition local adjutantID = heroArmyData.AdjutantID if adjutantID and adjutantID > 0 then EntityFactory.SetType_ID(LuaConst.CharacterType.Adjutant, adjutantID) EntityFactory.SetAttrTable_ID(attr) EntityFactory.SetEffectBreak_Weapon(heroArmyData.viceHeroBreakNums, heroArmyData.isViceMagicWeaponActive, heroArmyData.viceHeroWeaponLevels) EntityFactory.SetHeroAppearance_ID(heroArmyData.viceHeroAppearanceId) EntityFactory.SetWeaponAppearance_ID(heroArmyData.viceWeaponAppearanceId) EntityFactory.DoCreateAdjutantEntity(entity) end entity:SyncFinish() return entity end -- 计算机器人的属性 function SyncWorldBossLevelFunction:GetRobotAttribute(roleGsSum, heroId, adjutantID) local heroType = DataSheetManager.HeroNum:GetDataByID(heroId).TypeID -- 职业 local attr = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} -- 若不补充完整,则ipairs就中断了。 local heroAttrDic = FormulaCalculator.CalculateRobotEnemyPlayerAttribute(roleGsSum, Const_NotGuideRobotHeroNum, heroType, true) heroType = DataSheetManager.HeroNum:GetDataByID(adjutantID).TypeID -- 副将职业 local adjutantAttrDic = FormulaCalculator.CalculateRobotEnemyPlayerAttribute(roleGsSum, Const_NotGuideRobotHeroNum, heroType, false) -- 属性到[15] "anitfindmgvar" 伤害加成 不够 ,需要到 22-salmt(无双上限)。 for key, val in pairs(heroAttrDic) do attr[LuaConst.AttrTypeId[key]] = FP.tonumberV1(val + (adjutantAttrDic[key] or FP.Const.Zero) ) end return attr end function SyncWorldBossLevelFunction:GetHeroKillInvolvedTimeLimitFrame() return WorldBossKillConditionInFrame or Base.GetHeroKillInvolvedTimeLimitFrame(self) end --视图代码↓-------------------------------------------------- --视图代码↑-------------------------------------------------- --收到kcp的显示主将信息 function SyncWorldBossLevelFunction:SetPlayerActive(playerID) --不调用Base local playerHeroArmy = LevelPlayerManager.PlayersArmyTable[playerID] if not playerHeroArmy then logging.error(LTAG.ERROR, "【GWS】严重错误:创建主将时居然没有找到玩家战斗信息 playerID : ", playerID) return end local entities = self:CreatePlayerEntities(playerID ,playerHeroArmy) local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerID) if playerInfo.fighterType == LuaConst.BattleMemberType.Robot_Hero then self:StartAutoFight(entities[1]) --机器人默认开启AI end self:OnCreateNewPlayerFinish(playerID) local heroEntitys = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) local entity = heroEntitys[1] -- 如果是我自己,就打卡界面 if playerID == LevelPlayerManager.MyPlayerIDInRoom then LevelPlayerManager.SetCurEntity(entity, false, LevelPlayerManager.GetPlayerSetting(playerID, LuaConst.BattleSettingEnum.AutoRide)) else SystemManager.Active(entity, LevelPlayerManager.GetPlayerSetting(playerID, LuaConst.BattleSettingEnum.AutoRide)) self.exp:SetModelUIImageColorOfPlayerEntities(playerID, playerHeroArmy.playerInfo.campID, heroEntitys) end self:OnPlayerActived(playerID) end function SyncWorldBossLevelFunction:GetRobotAI(entity) local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(entity:GetPlayerID()) if entity._logicInitData.BattleMemberType == LuaConst.BattleMemberType.Robot_Hero then if playerInfo and playerInfo.isGuideRobot then --助战机器人用老的AI return LuaConst.WorldBossFightAIID else -- 服务器下发的假的玩家的AI return LuaConst.WorldBossRobotFightAIID end else --玩家用新的AI,策划需求玩家只需要盯着boss打 return LuaConst.TargetOnlyBossFightAIID end end function SyncWorldBossLevelFunction:GetKillNum(playerID) return private.RecordKillNumDic[playerID] or 0 end function SyncWorldBossLevelFunction:ClearKillNum(playerID) private.RecordKillNumDic[playerID] = nil end function SyncWorldBossLevelFunction:OnLoadFinished() Base.OnLoadFinished(self) end function SyncWorldBossLevelFunction:BeginLevel() Base.BeginLevel(self) end function SyncWorldBossLevelFunction:OnWorldBossSeekHelp() self:TryActiveTutorialAssistRobot() end function SyncWorldBossLevelFunction:OnUpdate() Base.OnUpdate(self) if private.IsDamageRecordDirty then self:RefreshDamageList() private.IsDamageRecordDirty = false end if #private.CurFrameHurtMainPlayerHeroes > 0 then --CurFrameHurtMainPlayerHeroes里面装的是entity EventSystem.Dispatch(EventID.MainPlayerGetHurtCurFrame,private.CurFrameHurtMainPlayerHeroes) for i=1,#private.CurFrameHurtMainPlayerHeroes do private.CurFrameHurtMainPlayerHeroes[i] = nil end end self:OnUpdateTryRobotActive() self:Update_RobotTryRevive() end function SyncWorldBossLevelFunction:OnUpdateTryRobotActive() if LogicUpdateManager._clientHandleFrame > Const_RobotRandomActiveMaxFrame then return end if self.robotRandomActiveDic[LogicUpdateManager._clientHandleFrame] then for i,playerID in ipairs(self.robotRandomActiveDic[LogicUpdateManager._clientHandleFrame]) do self:SetPlayerActive(playerID) end self.robotRandomActiveDic[LogicUpdateManager._clientHandleFrame] = nil end end function SyncWorldBossLevelFunction:GetDamageRecord(teamId) local value = nil for i,damageRecord in ipairs(private.DamageList) do if damageRecord.teamId == teamId then value = damageRecord break end end return value end function SyncWorldBossLevelFunction:GetDamageList() return private.DamageList end --返回排名,如果没有上榜 返回-1 function SyncWorldBossLevelFunction:GetDamageRankIndexByPlayerID(playerID) for i,v in ipairs(private.DamageList) do for j,_playerID in ipairs(v.playerIDList) do if _playerID == playerID then return i end end end return -1 end function SyncWorldBossLevelFunction:GetDamageOfPlayer(playerID) if private.RecordHpDic then return private.RecordHpDic[playerID] else return nil end end -- 获取所有玩家对boss的伤害统计 (只给服务器使用的代码),要使用这部分代码 ,需要在自己的fuction中统计 function SyncWorldBossLevelFunction:GetBossDamageRecord() --世界boss及其子类有自己的伤害统计,所以这里要重写基类 --不调用base local info = {} for _,playerID in ipairs(LevelPlayerManager.SyncPlayerIDList) do local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerID) -- 玩家基础信息 local statisticInfo = self.PlayerStatisticDic[playerID] -- 玩法统计数据 if statisticInfo then -- 有些玩家只传入了playerInfo ,没有创建entity ,所以就没有数据 local acid = playerInfo.acid local guildID = playerInfo.guildID -- 公会id local playerName = playerInfo.name local damage = math.floor(FP.tonumberV1(self:GetDamageOfPlayer(playerID) or FP.Const.Zero)) table.insert(info, acid) table.insert(info, guildID) table.insert(info, damage) table.insert(info, playerName) end end return info end local function DamageListSorter(a,b) if a.damage ~= b.damage then return a.damage > b.damage else if type(a.teamId) == type(b.teamId) then return a.teamId < b.teamId else return type(a) == "number" end end end function SyncWorldBossLevelFunction:RefreshDamageList() --先清空damage数据 for i,damageRecord in ipairs(private.DamageList) do damageRecord.damage:Reset() table.clear(damageRecord.playerIDList) table.clear(damageRecord.entityList) end local damageRecord = nil local _entity = nil local entityTable = nil local playerInfo = nil local teamId = nil local teamLeaderName = nil --下面会排序,所以这里可以用pairs for _playerID, _damage in pairs(private.RecordHpDic) do --pairs safe 统计伤害时序不敏感 if _playerID > 0 then --有可能有_playerID为-1的项,所以这里过滤掉 playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(_playerID) entityTable = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(_playerID) _entity = entityTable and entityTable[1] or nil teamId = _entity:GetTeamID() teamLeaderName = _entity:GetTeamLeaderName() damageRecord = self:GetDamageRecord(teamId) if not damageRecord then damageRecord = { damage = FP.newV1(0), playerIDList = {_playerID}, --entity = _entity, entityList = {_entity}, teamId = teamId, teamLeaderName = teamLeaderName, } table.insert(private.DamageList, damageRecord) else table.insert(damageRecord.playerIDList, _playerID) table.insert(damageRecord.entityList, _entity) end damageRecord.damage:Add(_damage) end end for i=#private.DamageList,1,-1 do if #private.DamageList[i].playerIDList == 0 then table.remove(private.DamageList, i) end end table.sort(private.DamageList, DamageListSorter) self.exp:RefreshNo1Icon(private.DamageList) EventSystem.Dispatch(EventID.UI_WorldBoss_UpdateDamageInfo, private.RecordHpDic) end function SyncWorldBossLevelFunction:ChangeRecordHpOfPlayer(playerID, delta) private.RecordHpDic[playerID] = private.RecordHpDic[playerID] or FP.newV1(0) private.RecordHpDic[playerID]:Add(delta) end function SyncWorldBossLevelFunction:OnHpChange(entity,caster,delta,op) --不调用Base 因为不需要统计助攻数 if self.isResultSent then --如果发送过结果了,则不再统计HpChange return end --如果是自己给自己造成伤害,直接return if entity == caster then return end if delta < FP.Const.Zero then if caster and caster:IsActive() then if entity:IsBoss() then local sourceEntity = caster:GetMasterEntity() or caster if sourceEntity:IsActive() then --这里必须判断是否active,因为人出去了,召唤物还会留在里面打伤害 local playerID = sourceEntity:GetPlayerID() if self.canHurtBossDic[playerID] then private.RecordHpDic[sourceEntity._logicInitData.PlayerIDInRoom] = private.RecordHpDic[sourceEntity._logicInitData.PlayerIDInRoom] or FP.newV1(0) private.RecordHpDic[sourceEntity._logicInitData.PlayerIDInRoom]:Sub(delta) private.IsDamageRecordDirty = true end end end if not self.isFightModeFixed then if entity:IsHero() and caster:IsHero() then local playerID = entity:GetPlayerID() local entities = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) for i,_entity in ipairs(entities) do _entity.components.target:AddDontIgnoreEnemyPlayerID(caster:GetPlayerID()) end self:RefreshFightModeOfPlayer(playerID, entity:GetFightMode()) end end --记录谁打了主玩家。这个是主视角代码,仅用于视图,不要用于逻辑 if entity:IsPlayerSelf() and caster:IsHero() then local exist = false for i,v in ipairs(private.CurFrameHurtMainPlayerHeroes) do if v == caster then exist = true break end end if not exist then table.insert(private.CurFrameHurtMainPlayerHeroes,caster) end end --↑ end end Base.OnHpChange(self, entity, caster, delta, op) end function SyncWorldBossLevelFunction:TryInitPlayerRecordHpDic(playerID) if not private.RecordHpDic[playerID] then private.RecordHpDic[playerID] = FP.newV1(0) end end function SyncWorldBossLevelFunction:RemoveRecordHpIfZero(playerID) --这是为了让通过抢旗进入伤害榜的玩家在丢失旗子时从伤害榜上移除 if private.RecordHpDic[playerID] and private.RecordHpDic[playerID] == FP.Const.Zero then private.RecordHpDic[playerID] = nil self:RefreshDamageList() end end function SyncWorldBossLevelFunction:SendBIWorldBossBeKilled(killerName) EventSystem.Dispatch(EventID.BI_LevelHigh,BIManager.BIType.WorldBossBeKilled, killerName) end function SyncWorldBossLevelFunction:OnHeroDead(entity) Base.OnHeroDead(self, entity) if not entity.components.record.KillerEntity then --好像有些情况关卡会杀Hero,且这种情况下KillerEntity为空,所以这里要判空 return end if entity.components.record.KillerEntity:IsHero() then self.allPlayerHeroDeadCount = self.allPlayerHeroDeadCount + 1 self:RefreshKillRecord(entity) elseif entity.components.record.KillerEntity:IsBoss() then local dStatistic = self.PlayerStatisticDic[entity:GetPlayerID()] if dStatistic then dStatistic[LuaConst.EStatisticType.PlayerBeKilledByBossCount] = dStatistic[LuaConst.EStatisticType.PlayerBeKilledByBossCount] + 1 end end if entity:IsPlayerSelf() then --主视角safe,纯dispatch给UI local killerName = entity.components.record.KillerEntity._logicInitData.Name if string.isNilOrEmpty(killerName) then killerName = entity.components.record.KillerEntity.components.base.CurCharacterData.MonsterNameIDS end -- 大数据统计 self:SendBIWorldBossBeKilled(killerName) end local deadPlayerID = entity:GetPlayerID() local comboKillNumBeforeDead = private.RecordComboKillNumDic[deadPlayerID] or 0 -- 死者为玩家 if entity._logicInitData.IsPlayer then -- 这段移到ExpSyncWorldBossLevelFunction:OnHandleHeroDead里去了↓ -- --派发自己击杀其他玩家的事件 local recordComp = entity.components.record -- if(recordComp.KillerEntity == LogicUpdateManager._curEntity) then -- EventSystem.Dispatch(EventID.UI_WorldBoss_Kill, entity) -- 显示tips -- else -- if recordComp.KillerEntity then -- local killerPlayerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(recordComp.KillerEntity:GetPlayerID()) -- if killerPlayerInfo then -- if killerPlayerInfo.assisAimAcid and killerPlayerInfo.assisAimAcid ~= "" then -- local assisAimPlayerInfo = LevelPlayerManager.GetPlayerInfoByAcid(killerPlayerInfo.assisAimAcid) -- if assisAimPlayerInfo then -- FirstPersonEventDispatcher.OnAssistantKill(assisAimPlayerInfo) -- else -- logging.error(LTAG.ERROR, "HYW : assisAimPlayerInfo nil Error @SyncWorldBossLevelFunction:OnHeroDead") -- end -- end -- --else -- --正常现象,Killer不一定是玩家 -- end -- end -- end -- 这段移到ExpSyncWorldBossLevelFunction:OnHandleHeroDead里去了↑ --↓这段移到 SyncWorldBossLevelFunction:AfterHanleHeroDead 里去了 -- -- 统计最后一刀的数量 -- local killerPlayerID = recordComp.KillerEntity._logicInitData.PlayerIDInRoom -- 杀手ID -- if private.RecordKillNumDic[killerPlayerID] then -- boss 或者小兵 不记录 -- private.RecordKillNumDic[killerPlayerID] = private.RecordKillNumDic[killerPlayerID] + 1 -- private.RecordComboKillNumDic[killerPlayerID] = private.RecordComboKillNumDic[killerPlayerID] + 1 -- end --↑这段移到 SyncWorldBossLevelFunction:AfterHanleHeroDead 里去了 if private.RecordComboKillNumDic[deadPlayerID] then private.RecordComboKillNumDic[deadPlayerID] = 0 end end EventSystem.Dispatch(EventID.WorldBoss_PlayerAllHeroBeKilled, entity, comboKillNumBeforeDead) end function SyncWorldBossLevelFunction:AfterHanleHeroDead(killer, deadEntity, involveKillPlayerIDList, assistHeroEntityIDList, kStatistic, onlyKillerAddKillNum) Base.AfterHanleHeroDead(self, killer, deadEntity, involveKillPlayerIDList, assistHeroEntityIDList, kStatistic, onlyKillerAddKillNum) if onlyKillerAddKillNum then local killerPlayerID = killer:GetPlayerID() private.RecordKillNumDic[killerPlayerID] = (private.RecordKillNumDic[killerPlayerID] or 0)+ 1 private.RecordComboKillNumDic[killerPlayerID] = (private.RecordComboKillNumDic[killerPlayerID] or 0) + 1 else --策划改了世界boss、战区boss的击杀的规则,新需求是需要给所有参与击杀的人添加击杀数和连杀数 --2022年9月19日 for i,playerID in ipairs(involveKillPlayerIDList) do if private.RecordKillNumDic[playerID] then private.RecordKillNumDic[playerID] = private.RecordKillNumDic[playerID] + 1 private.RecordComboKillNumDic[playerID] = private.RecordComboKillNumDic[playerID] + 1 end end end end function SyncWorldBossLevelFunction:OnHeroKillHero(killer,deadEntity, deadComboKill) Base.OnHeroKillHero(self, killer, deadEntity, deadComboKill) for i,v in ipairs(Const_KillerDeBuff) do SystemManager.AddBuff(killer, v) SystemManager.DeleteBuff(deadEntity, v) end end -- 机器人需要能自动切换武将,如果全部死亡,则每n秒检测一次是否有武将复活可用 不调用Base function SyncWorldBossLevelFunction:OnRobotHeroDead_SelectOne(entity) local entitys = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(entity._logicInitData.PlayerIDInRoom) if entitys then local find = false for index,toEntity in ipairs(entitys) do if toEntity.components.attr:IsAlive() then LevelPlayerManager.SwitchHero(entity._logicInitData.PlayerIDInRoom,index , true) find = true break end end if not find then table.insert(self.waitReviewRobotList, entity._logicInitData.PlayerIDInRoom) --logging.error(LTAG.ERROR, "gws : 机器人死亡,没有找到可以切换的武将 , pid = ", entity._logicInitData.PlayerIDInRoom) end end end -- 每n帧检测一次是否有机器人可以复活 (仿写的TryReviveAndSwitchOneHero) function SyncWorldBossLevelFunction:Update_RobotTryRevive() if LogicUpdateManager._clientHandleFrame % Const_RobotReviveCheckFrame == 0 then if #self.waitReviewRobotList > 0 then for i = #self.waitReviewRobotList, 1, -1 do local playerID = self.waitReviewRobotList[i] local armyTable = LevelPlayerManager.GetPlayerArmyByPlayerIDInRoom(playerID) if armyTable then local entities = LevelPlayerManager.PlayersEntityTable[playerID] for index = 1, #armyTable do local entity = entities[index] local heroID = armyTable[index].HeroID local armyPos = LevelPlayerManager.GetHeroArmyPos(playerID,heroID) local deadInfo = self.playerHeroDeadTimeFrame[playerID][armyPos] if not deadInfo.ReviveFrame or deadInfo.ReviveFrame <= LogicUpdateManager._clientHandleFrame then -- deadInfo.ReviveFrame为nil表示hero从来没死过 SystemManager.OnRevive(entity, self.isSwitchOneReviveNeerBy, nil, self.shouldReviveAtNearestBornPoint) local errorCode = LevelPlayerManager.SwitchHero(playerID, index, true) if errorCode == 0 or errorCode == 2 then --errorCode == 2 表示复活当前死亡的英雄,也需要抛出事件 if errorCode == 2 then BattleEventSystem.AfterSelectOneReviveCurHero(entities[1]) -- 这个事件一定是当前entity作为参数抛出 end BattleEventSystem.AfterSelectOneRevive(entity) end EventSystem.Dispatch(EventID.UI_SelectOneReviveOver, entity) table.remove(self.waitReviewRobotList, i) --logging.error(LTAG.ERROR, "gws : 机器人 复活 武将 啦! , pid = ", playerID, index) break end end end end end end end --↓↓↓↓↓↓击杀信息处理新需求 2022年1月13日 function SyncWorldBossLevelFunction:RefreshKillRecord(entity) local killRecord local killerPlayerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(entity.components.record.KillerEntity:GetPlayerID()) local killerAcid = killerPlayerInfo.acid local deadPlayerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(entity:GetPlayerID()) local deadAcid = deadPlayerInfo.acid for i,record in ipairs(self.KillRecordList) do if record.killerAcid == killerAcid and record.deadAcid == deadAcid then killRecord = record break end end if not killRecord then killRecord = { killerName = killerPlayerInfo.name, killerAcid = killerAcid, deadName = deadPlayerInfo.name, deadAcid = deadAcid, times = 0, firstFrameNum = LogicUpdateManager._clientHandleFrame, } table.insert(self.KillRecordList, killRecord) end killRecord.times = killRecord.times + 1 killRecord.lastFrameNum = LogicUpdateManager._clientHandleFrame end --↑↑↑↑↑↑击杀信息处理 function SyncWorldBossLevelFunction:SwtichForceTargetByIndex(entity, index) local targetDmgRecord = private.DamageList[index] --点击的玩家的damageRecord local teamId = entity:GetTeamID() local playerID = entity:GetPlayerID() if targetDmgRecord.teamId ~= teamId then --如果点击的不是自己 local isSet = TargetManager.SwtichForceTeam(playerID, targetDmgRecord.teamId) local oldForceTeamId = self:GetForceTeamId(playerID) local oldTargetDmgRecord = self:GetDamageRecord(oldForceTeamId) if isSet then local entityTable = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) SystemManager.StartAutoFight(entityTable[1]) self:SetForceTeamId(playerID, targetDmgRecord.teamId) else self:SetForceTeamId(playerID, nil) end self.exp:OnSwitchForceTeamByIndex(isSet, entity, oldTargetDmgRecord, targetDmgRecord) return targetDmgRecord.teamId,isSet end return nil end function SyncWorldBossLevelFunction:QuitRoom(entity) --不调用base self:QuitRoomAndNotRetain(entity) private.RecordHpDic[entity._logicInitData.PlayerIDInRoom] = nil -- ↓↓↓↓↓↓更新伤害记录列表(当entity == LogicUpdateManager._curEntity时,下面的代码是冗余操作,但是为了显而易见的帧同步代码一致性,统一做了) local playerID = entity:GetPlayerID() local teamId = entity:GetTeamID() local teamRemoved = false --删除伤害记录 for listIndex = #private.DamageList,1,-1 do local damageRecord = private.DamageList[listIndex] if damageRecord.teamId == teamId then for j, _playerID in ipairs(damageRecord.playerIDList) do if _playerID == playerID then table.remove(damageRecord.playerIDList, j) for m, _entity in ipairs(damageRecord.entityList) do if entity == _entity then table.remove(damageRecord.entityList, m) break end end if #damageRecord.playerIDList == 0 then table.remove(private.DamageList, listIndex) teamRemoved = true end break end end break end end -- ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ if entity == LogicUpdateManager._curEntity then private.RecordReliveTimes = private.RecordReliveTimes + 1 EventSystem.Dispatch(EventID.BI_LevelHigh,BIManager.BIType.WorldBossRelive,private.RecordReliveTimes ,3) if not LogicUpdateManager.IsReplayOrServerCheck then -- 客户端重播或服务器校验的话,不要执行下面代码 EventSystem.Dispatch(EventID.Level_SendRoleLeaveFuben,true) self:StopLevel() end else if teamRemoved then self:OnTeamQuitRoom(teamId) end private.IsDamageRecordDirty = true EventSystem.Dispatch(EventID.UI_WorldBoss_PlayerQuitRoom,entity._logicInitData.PlayerIDInRoom) end end function SyncWorldBossLevelFunction:OnTeamQuitRoom(teamId) self.exp:OnTeamQuitRoom(teamId) end function SyncWorldBossLevelFunction:SetForceTeamId(playerID, forceTeamId) private.PlayerID2ForceTeamIdDic[playerID] = forceTeamId end function SyncWorldBossLevelFunction:GetForceTeamId(playerID) return private.PlayerID2ForceTeamIdDic[playerID] end function SyncWorldBossLevelFunction:GetDefaultFightMode(worldBossId) local worldBossData = DataSheetManager.WorldBoss:GetDataByID(worldBossId) return worldBossData and worldBossData.PKMode or LuaConst.EFightMode.PEACE end function SyncWorldBossLevelFunction:TryReviveAndSwitchOneHero(entity,index) local canRevive = Base.TryReviveAndSwitchOneHero(self,entity,index) if canRevive then if entity:IsPlayerSelf() then private.RecordReliveTimes = private.RecordReliveTimes + 1 EventSystem.Dispatch(EventID.BI_LevelHigh,BIManager.BIType.WorldBossRelive,private.RecordReliveTimes ,0) end if entity:IsHero() then if not self.isFightModeFixed then self:RefreshAllFightMode() end end end return canRevive end function SyncWorldBossLevelFunction:OnFightModeChanged(entity, fightMode) self.exp:OnFightModeChanged(entity, fightMode) end function SyncWorldBossLevelFunction:GetLevelAutoFightInitRule() --不调用base return LuaConst.ELevelAutoFightInitRule.OPEN end function SyncWorldBossLevelFunction:SwitchAutoFight(entity) Base.SwitchAutoFight(self,entity) local heroEntitys = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(entity:GetPlayerID()) for i,_entity in ipairs(heroEntitys) do _entity.components.target:SetForceTarget(nil) end end function SyncWorldBossLevelFunction:OnSwitchHero(fromEntity,toEntity) Base.OnSwitchHero(self, fromEntity, toEntity) local switchPlayerID = fromEntity:GetPlayerID() local damageRecord = self:GetDamageRecord(switchPlayerID) if damageRecord then for i,entity in ipairs(damageRecord.entityList) do entity.ExpEntity:SetModelNo1IconVisible(false) if entity:GetPlayerID() == switchPlayerID then damageRecord.entityList[i] = toEntity end end end local teamId = fromEntity:GetTeamID() local playerEntitys = nil for playerID, forceTeamId in pairs(private.PlayerID2ForceTeamIdDic) do --pairs safe SetTarget时序不敏感 if forceTeamId == teamId then playerEntitys = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) if playerEntitys[1] then SystemManager.SetTarget(playerEntitys[1], toEntity) end end end private.IsDamageRecordDirty = true --这里会延迟一帧刷新第一名的icon 直接SetModelNo1IconVisible(true)会有问题,可能是因为toEntity还没有激活 end function SyncWorldBossLevelFunction:CanHurtBoss(playerInfo) return playerInfo.wbRewardLeft > 0 and not self:IsAssitant(playerInfo) and playerInfo.fighterType ~= LuaConst.BattleMemberType.Chase_Player end function SyncWorldBossLevelFunction:OnCreateNewPlayerFinish(playerID) local entityTable = nil local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerID) local canHurtBoss = self:CanHurtBoss(playerInfo) self.canHurtBossDic[playerID] = canHurtBoss entityTable = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) if not canHurtBoss then for i,entity in ipairs(entityTable) do SystemManager.AddBuff(entity,IgnoreBossBuffID,entity,nil,nil) end end if not self.isFightModeFixed then if self:IsAssitant(playerInfo) or playerInfo.fighterType == LuaConst.BattleMemberType.Chase_Player then --new --协助者、追杀者 战斗模式设置为强制 SystemManager.SetFightMode(playerID, LuaConst.EFightMode.TEAM_GUILD, true) else --正常玩家(非协助者)的战斗模式设置为关卡表里配的默认模式 SystemManager.SetFightMode(playerID, self:GetDefaultFightMode(playerInfo.wbId), true) end --刷新所有单位的ignore关系 self:RefreshAllFightMode() end private.IsDamageRecordDirty = true self:TryCreatePlayerStatistic(playerID) self:TryAddPlayerBuff(playerID) end function SyncWorldBossLevelFunction:OnLevelSucceed(withShow) --发送结算协议 ,关闭KCP self:SendBattleResult(true) Base.OnLevelSucceed(self) end function SyncWorldBossLevelFunction:OnLevelFail() --发送结算协议 ,关闭KCP self:SendBattleResult(false) Base.OnLevelFail(self) if not LogicUpdateManager.IsReplayOrServerCheck then EventSystem.Dispatch(EventID.Level_SendRoleLeaveFuben) end end --获取每个玩家的剩余血量 function SyncWorldBossLevelFunction:GetAllPlayerHerosHP() local strDic = {} for i, damageRecord in ipairs(private.DamageList) do for j, playerID in ipairs(damageRecord.playerIDList) do local entityTable = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) local str = "" for _,entity in ipairs(entityTable) do local heroID = entity.components.base.CurCharacterData.ID local hp = tostring(FP.tonumberV1(entity.components.attr:HitPointPercents() ) ) str = str .. heroID ..":".. hp .. ";" end strDic[playerID] = str end end return strDic end -- 获取最后结束时的校验字符串 function SyncWorldBossLevelFunction:GetCheckStr() local checkStr = "-"-- 服务器认为""有特殊含义 for i,damageRecord in ipairs(private.DamageList) do for j,playerID in ipairs(damageRecord.playerIDList) do -- old↓ --基类搞了一个通用方法,这里删了 -- local entity = damageRecord.entityList[j] -- local moveComp = entity.components.move -- local str = string.format("%d,f:%d, px:%d,pz:%d,fx:%d,fz:%d,Anim:%s,hp:%d|" -- , entity._logicInitData.EntityID,BattleDataGetter.GetCurFrameIndex() ,moveComp.LogicPos.x[1], moveComp.LogicPos.z[1],moveComp.LogicRotateDir.x[1], moveComp.LogicRotateDir.z[1],tostring(entity.components.anim.CurAnimID),entity.components.attr.hp[1]) -- old↑ -- new↓ 使用基类的通用方法 local str = self:GetBattleResultCheckStrOfEntity(damageRecord.entityList[j]) -- new↑ checkStr = checkStr .. str end end return checkStr end --设置结算协议 function SyncWorldBossLevelFunction:SendBattleResult(isSuccess) self.isResultSent = true logging.log(LTAG.LOG ,"[worldBoss] : SendWorldBossResult ") local playerHerosHPDic = self:GetAllPlayerHerosHP() local checkStr = self:GetCheckStr() local msg = world_boss_pb.C2GUploadWorldBossResult() --之前只统计有boss伤害和有击杀记录的玩家信息 。现在房间中的所有玩家(同acid发多个)都统计 。服务器受到的影响是:1.排序0伤害多个 2.BI统计伤害会有空值 。这两个影响很小,不用考虑 -- 服务器有问题找maxiaochen,他说这样改没问题的 for _, playerID in ipairs(LevelPlayerManager.SyncPlayerIDList) do local fighter = world_boss_pb.FighterWBResult() fighter.playerID = playerID fighter.damage = private.RecordHpDic[playerID] and math.floor(FP.tonumberV1(private.RecordHpDic[playerID])) or 0 fighter.heroLeftHp = playerHerosHPDic[playerID] or "" fighter.killNum = private.RecordKillNumDic[playerID] or 0 local playerStatistic = self.PlayerStatisticDic[playerID] if playerStatistic then fighter.beKilledTimesByBoss = playerStatistic[LuaConst.EStatisticType.PlayerBeKilledByBossCount] end self:FillBeKilledInfo(fighter, playerID) private.RecordKillNumDic[playerID] = nil table.insert(msg.allFighterRes ,fighter) end msg.whoKilledBoss = 0 --杀死boss的人,暂时不传,服务器也么有用 ,等策划订好怎么用时再想办法加 msg.checkInfo = checkStr msg.success = isSuccess if LogicUpdateManager.IsReplayOrServerCheck then -- 客户端重播或服务器校验的话,不要发送TCP logging.log(LTAG.LOG,"[GWS]SyncWorldBoss BattleCheckStr = ",checkStr) else EventSystem.Dispatch(EventID.WorldBoss_SendResult,msg) end if USE_LUA_STANDALONE then BattleSimulator.SetCheckResult(checkStr, msg:SerializeToString()) end end --新增传给服务器的击杀信息 function SyncWorldBossLevelFunction:FillBeKilledInfo(fighter, playerID) local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerID) for i,record in ipairs(self.KillRecordList) do if record.deadAcid == playerInfo.acid then --logging.error(LTAG.ERROR, "HYW : 填充击杀信息: 击杀者:", record.killerName, " 死者:", record.deadName, " 击杀次数:", record.times) local fighterWBBeKilledInfo = world_boss_pb.FighterWBBeKilledInfo() fighterWBBeKilledInfo.fighterName = record.killerName fighterWBBeKilledInfo.playerACID = record.killerAcid fighterWBBeKilledInfo.times = record.times fighterWBBeKilledInfo.framesNum = record.firstFrameNum table.insert(fighter.FighterWBBeKilledInfos, fighterWBBeKilledInfo) end end end return SyncWorldBossLevelFunction