local EventSystem = require('Event/EventSystem') local Base = require("Battle/Logic/Level/LevelFunction/LevelFunction") local SyncLevelFunction = class("SyncLevelFunction",Base) local private = {} local BattleLogSetter = require("Battle/BattleLogSetter") local ELevelWaitingPhase = LuaConst.ELevelWaitingPhase --3v3同步玩法,等待开始时间 (人齐后,等待fight的时长) local SyncWaitingCountDownFrame = 100 SyncLevelFunction.isSyncLevel = true local MAX_PLAYER_TO_BOSS_DMG_RECORD_COUNT = 1000 --最多存多少个伤害记录 (playerToBossDmgRecord的最大长度 = MAX_PLAYER_TO_BOSS_DMG_RECORD_COUNT * 3 + 1) local EStatisticType = LuaConst.EStatisticType local PlayerHangUpState = LuaConst.PlayerHangUpState local Const_Vector2_Zero = FP.Const.Vector2_Zero local MaxInputSpecialCommand = LuaConst.SpecialCommandType.MaxByManual local Const_WaitOthersFrame = DataSheetManager.ConstData:GetDataByID("TVTWaitTime").IntValue * LuaConst.LogicFrameRate local Const_PunishForAFKTime1 = DataSheetManager.ConstData:GetDataByID("PunishForAFKTime1").IntValue * LuaConst.LogicFrameRate -- 挂机到 ai托管的时长 local Const_PunishForAFKTime2 = DataSheetManager.ConstData:GetDataByID("PunishForAFKTime2").IntValue * LuaConst.LogicFrameRate -- ai托管到惩罚的时长 local Const_PunishForAFKTime3 = Const_PunishForAFKTime1 + Const_PunishForAFKTime2 -- 从挂机到惩罚的时长 local SimpleSorter = function(a, b) return a < b end function SyncLevelFunction:GetExpClass() return require "Battle/Expression/ExpLevel/ExpLevelFunction/ExpSyncLevelFunction" end function SyncLevelFunction:initialize() Base.initialize(self) self.syncTimelineIndex = 0 --联机播放timeline的序号,表示目前播过几个timeline end function SyncLevelFunction:InitLevel() self.isFreeToEnterAndLeave = false --默认不是随进随出 self.relaxKillConditionFrame = DataSheetManager.ConstData:GetDataByID("RelaxKillInFrame").IntValue -- 放宽击杀条件时:n秒内的助攻算为击杀。n <= GetHeroKillInvolvedTimeLimitFrame --是否需要记录玩家对boss的伤害信息 ↓ 目前是晚间boss需要记录,用来判断跨房间的尾刀(2023年1月16日) --会记录在 self.playerToBossDmgRecord里 数据结构为 {pointer,playerID,damage,frame,acid,playerID,damage,frame,acid,...},第1个元素是最后的指针位置,从第2个元素开始是playerID、伤害、发生的帧数、玩家的acid间隔重复) --self.needRecordPlayerToBossDamage = false --↑↑↑ self.RobotList = {}--机器人列表,包含假数据创建的机器人,以及离线状态的玩家的英雄单位 self.firstOnlineNeedStopAutoFightDic = {} -- 在等人阶段没有进入的且开始自动战斗的玩家列表(入场时掉线托管列表) 。用于:一进入时 ,停止自动战斗(接管) self:TryRefreshTeamLeaderInfo() Base.InitLevel(self) self.addBuffServerActionRecords = {} -- self.bossAddBuffServerActionRecords = {} --给全体boss加buff的serverBattleAction,做一个记录,在游戏后面刷新出来的boss也要加上这些buff -- self.heroAddBuffServerActionRecords = {} --给全体hero加buff的serverBattleAction,做一个记录,在游戏后面刷新出来的hero也要加上这些buff self.HadStopLevel = false -- 标记已经调用了stopLevel --↓伤害时间戳统计, key是受伤的EntityID ,value是伤害时间戳记录(DamageTimestampRecord) --value由CreateDamageTimestampRecord方法创建,包含player的伤害时间戳,以及各个英雄的伤害时间戳(因为策划要统计每个英雄的助攻) --key是受伤的单位,value是一个数据table,包含施加伤害的player的伤害时间戳 和 施加伤害的单位的伤害时间戳 self.DmgTimestampRecordDic = {} -- 伤害时间戳统计 key - 受伤的EntityID ,value = { playerDmgTimestampDic:key为playerID,value为帧号; heroDmgTimestampDic:key为entityID,value为帧号} (包括最后一刀人) --↑ self.curWaitingPhase = nil --等待阶段 有的关卡不需要等待,则这个值一直为nil self.curWaitingPhaseFrameEla = 0 --等待单阶段已持续帧数,注意是单阶段的,不是全阶总和 self.syncWaitingCountDownFrame = SyncWaitingCountDownFrame -- 等待开始战斗时长(单位帧) self.serverBattleActionRequestList = {} --校验程序发起的,由服务器处理的插帧请求 self.shouldWaitOtherPlayer = LevelManager.GetWaitOtherPlayerType() ~= 0 self.forceWaitOtherPlayerPhase = false -- 非随进随出玩法是否等待他人进入 self.RivalInfoDic = {} -- 玩家之间的仇敌关系 { [playerID] = { [acid] = RivalType ,[acid] = RivalType} , } self.guildVisualDic = {} --势力共享视野 key为guildID,value为一个数组,数组的元素是一个table : {playerID, visualLevel, visualGroup}. 同势力的所有单位拥有guildID对应的数组里的所有视野 self.sameGuildCanBeEnemy = LevelManager.IsFairLevel() and true or false --联机公平玩法同势力的玩家可以成为敌对阵营,非公平玩法不能 self.countryJobBuffDic = {} -- 提供国家buff的信息字典 key:国家ID ,val: {sourcePlayerID, maxCountryJob} self.watchCheckStr = nil -- 观战到最后用到的校验字符串 self.needRecordPlayerHangUp = false -- 本关卡是否需要记录玩家挂机了【挂机惩罚:需要给到玩家很大的宽容度,掉线、死亡、掉帧等不算挂机】 self.playerHangUpStateDic = {} -- 负面态度的玩家列表,key为playerID,value为当前挂机状态PlayerHangUpState self.playerHangUpFrameDic = {} -- 统计玩家的连续挂机时长,key为playerID,value为挂机时长(单位帧) end --开启玩家对boss每次造成伤害的记录,只会记录MAX_PLAYER_TO_BOSS_DMG_RECORD_COUNT个 --注意这个记录的是每次玩家对boss造成伤害的伤害量、时间戳等数据,而不是总伤害统计(总伤害统计是 GetBossDamageRecord) function SyncLevelFunction:OpenPlayerToBossDamageRecord() self.needRecordPlayerToBossDamage = true self.playerToBossDmgRecord = {} self.playerToBossDmgRecordPointerPos = 1 --指针位置,下次数据会从 self.playerToBossDmgRecordPointerPos + 1 开始写入,所以该值不会等于 self.playerToBossDmgRecordMaxLength,值域: [1, self.playerToBossDmgRecordMaxLength - 4] self.playerToBossDmgRecord[1] = self.playerToBossDmgRecordPointerPos self.playerToBossDmgRecordMaxLength = MAX_PLAYER_TO_BOSS_DMG_RECORD_COUNT * 4 + 1 --整个数组的第1位是指针位置,从第2位开始每次伤害记录占4位,,self.playerToBossDmgRecord会循环使用,所以记录指针位置 end function SyncLevelFunction:TryRefreshTeamLeaderInfo() if not LevelManager.IsFreeToEnterAndLeaveLevel() then --策划要求:世界Boss、军团战以外的联机玩法(现在改为非随进随出玩法),都得判断是否需要设置队长为playerID为1的人,方便播timeline local anyTeamLeader local hasSameTeamLeader = true for k,v in pairs(LevelPlayerManager.PlayersArmyTable) do if anyTeamLeader and anyTeamLeader ~= v.playerInfo.teamLeaderName then hasSameTeamLeader = false break end anyTeamLeader = v.playerInfo.teamLeaderName end if not anyTeamLeader or anyTeamLeader == "" or not hasSameTeamLeader then --如果没有任何队长,或者队长不一致,则把队长设置为player1 LevelPlayerManager.SetTeamLeaderNameToPlayer1() end end end function SyncLevelFunction:TryCreateRobots() local playerInfo = nil for i,playerID in ipairs(LevelPlayerManager.SyncPlayerIDList) do playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerID) if playerInfo.fighterType == LuaConst.BattleMemberType.Robot_Hero then self:CreateRobot(playerID) end end end function SyncLevelFunction:CreateRobot(playerID) local entities = self:CreatePlayerEntities(playerID, LevelPlayerManager.PlayersArmyTable[playerID]) self:StartAutoFight(entities[1]) --机器人默认开启AI end function SyncLevelFunction:CreatePlayers() if not self.isFreeToEnterAndLeave then LevelPlayerManager.PvpPlayerCount = 0 local playerIDs = LevelPlayerManager.SyncPlayerIDList for i,playerId in ipairs(playerIDs) do local heroEntityTable = {} local isPlayerSelf = false if playerId == LevelPlayerManager.MyPlayerIDInRoom then isPlayerSelf = true end local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerId) local heroArmyDatas = LevelPlayerManager.GetPlayerArmyByPlayerIDInRoom(playerId) local totalGS = 0 for _,heroArmyData in ipairs(heroArmyDatas) do totalGS = totalGS + heroArmyData.heroGs end for _,heroArmyData in ipairs(heroArmyDatas) do local entity = self:CreatePlayerEntity(playerInfo,heroArmyData,isPlayerSelf,playerId) -- 玩家id对应其出生点 entity:SetGs(totalGS) table.insert(heroEntityTable,entity) end if playerInfo.fighterType == LuaConst.BattleMemberType.AI_Hero or playerInfo.fighterType == LuaConst.BattleMemberType.Account_Hero or playerInfo.fighterType == LuaConst.BattleMemberType.Called_Player then LevelPlayerManager.PvpPlayerCount = LevelPlayerManager.PvpPlayerCount + 1 end LevelPlayerManager.SetPlayerEntityTable(heroEntityTable ,playerId) local heroEntitys = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerId) -- 设置要操作的人 if isPlayerSelf then LevelPlayerManager.SetCurEntity(heroEntitys[1] , true) end self:OnCreatePlayer(playerId, playerInfo, heroEntitys[1]) end else --随进随出玩法的CreatePlayers流程只创建机器人 self:TryCreateRobots() end end function SyncLevelFunction:GetBornIndex(playerInfo) return ((playerInfo.playerID - 1) % LevelManager.bornPointCount) + 1 end function SyncLevelFunction:CreatePlayerEntities(playerId) local heroEntityTable = {} local isPlayerSelf = playerId == LevelPlayerManager.MyPlayerIDInRoom local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerId) local heroArmyDatas = LevelPlayerManager.GetPlayerArmyByPlayerIDInRoom(playerId) local totalGS = 0 for _,heroArmyData in ipairs(heroArmyDatas) do totalGS = totalGS + heroArmyData.heroGs end local bornIndex = self:GetBornIndex(playerInfo) if bornIndex == 0 then bornIndex = LevelManager.bornPointCount end for _,heroArmyData in ipairs(heroArmyDatas) do local entity = self:CreatePlayerEntity(playerInfo, heroArmyData, isPlayerSelf, bornIndex) -- 默认出生点 entity:SetGs(totalGS) table.insert(heroEntityTable,entity) end LevelPlayerManager.SetPlayerEntityTable(heroEntityTable, playerId) local firstEntity = heroEntityTable[1] self:OnCreatePlayer(playerId, playerInfo, firstEntity) local onHorse = LevelPlayerManager.GetPlayerSetting(playerId ,LuaConst.BattleSettingEnum.AutoRide) if isPlayerSelf then LevelPlayerManager.SetCurEntity(firstEntity, false, onHorse) else SystemManager.Active(firstEntity, onHorse) end return heroEntityTable end --某些玩法比如世界boss没有用这个生命周期 function SyncLevelFunction:OnCreatePlayer(playerID, playerInfo, firstEntity) --不调用Base self:TryCreatePlayerStatistic(playerID) self:TryAddPlayerBuff(playerID) EventSystem.Dispatch(EventID.Level_OnCreatePlayerAllEntitys,playerID) end function SyncLevelFunction:InitPlayerStatistic(statistic) Base.InitPlayerStatistic(self, statistic) statistic[EStatisticType.HeroKillNum] = 0 statistic[EStatisticType.InvolvedHeroKillNum] = 0 statistic[EStatisticType.AssistHeroKillNum] = 0 statistic[EStatisticType.CurComboHeroKillNum] = 0 statistic[EStatisticType.MaxComboHeroKillNum] = 0 statistic[EStatisticType.RelaxHeroKillNum] = 0 statistic[EStatisticType.RelaxAssistHeroKillNum] = 0 statistic[EStatisticType.RelaxCurComboHeroKillNum] = 0 statistic[EStatisticType.RelaxMaxComboHeroKillNum] = 0 statistic[EStatisticType.Hero1KillNum] = 0 statistic[EStatisticType.Hero2KillNum] = 0 statistic[EStatisticType.Hero3KillNum] = 0 statistic[EStatisticType.Hero4KillNum] = 0 statistic[EStatisticType.Hero5KillNum] = 0 statistic[EStatisticType.Hero6KillNum] = 0 statistic[EStatisticType.RelaxHero1KillNum] = 0 statistic[EStatisticType.RelaxHero3KillNum] = 0 statistic[EStatisticType.RelaxHero5KillNum] = 0 statistic[EStatisticType.Hero1InvolvedKillNum] = 0 statistic[EStatisticType.Hero2InvolvedKillNum] = 0 statistic[EStatisticType.Hero3InvolvedKillNum] = 0 statistic[EStatisticType.Hero4InvolvedKillNum] = 0 statistic[EStatisticType.Hero5InvolvedKillNum] = 0 statistic[EStatisticType.Hero6InvolvedKillNum] = 0 statistic[EStatisticType.Hero1AssistHeroKillNum] = 0 statistic[EStatisticType.Hero2AssistHeroKillNum] = 0 statistic[EStatisticType.Hero3AssistHeroKillNum] = 0 statistic[EStatisticType.Hero4AssistHeroKillNum] = 0 statistic[EStatisticType.Hero5AssistHeroKillNum] = 0 statistic[EStatisticType.Hero6AssistHeroKillNum] = 0 statistic[EStatisticType.RelaxHero1AssistHeroKillNum] = 0 statistic[EStatisticType.RelaxHero3AssistHeroKillNum] = 0 statistic[EStatisticType.RelaxHero5AssistHeroKillNum] = 0 statistic[EStatisticType.Hero1DeadCount] = 0 statistic[EStatisticType.Hero2DeadCount] = 0 statistic[EStatisticType.Hero3DeadCount] = 0 statistic[EStatisticType.Hero4DeadCount] = 0 statistic[EStatisticType.Hero5DeadCount] = 0 statistic[EStatisticType.Hero6DeadCount] = 0 end function SyncLevelFunction:GetBornItem(playerInfo, index) return LevelItemManager.GetBornItem(index) end -- 这个函数最好在其子类中都用这个,base.CreatePlayerEntity(self,playerInfo, heroArmyData,isPlayerSelf,index) function SyncLevelFunction:CreatePlayerEntity(playerInfo, heroArmyData, isPlayerSelf, 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 -- new↑ -- old↓-- 固定位置出生 -- local pos = playerInfo.Pos or bornItem.ItemData.ItemPos -- old↑ pos = pos:cloneV2() if LevelManager.IsGuildWarLevel() then --军团战临时代码 让机器人有偏移 if playerInfo.fighterType == LuaConst.BattleMemberType.Robot_Hero then pos:Add((FP.Const.Vector2_Half * (playerInfo.playerID - 7))) end end local dir = playerInfo.Dir or bornItem.ItemData.ItemDir local attr = heroArmyData.Attr 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(isPlayerSelf, 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.SetDyeAppearanceList(heroArmyData.mainDyeCurPartList) 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(heroArmyData.Attr) EntityFactory.SetEffectBreak_Weapon(heroArmyData.viceHeroBreakNums, heroArmyData.isViceMagicWeaponActive, heroArmyData.viceHeroWeaponLevels) EntityFactory.SetHeroAppearance_ID(heroArmyData.viceHeroAppearanceId) EntityFactory.SetWeaponAppearance_ID(heroArmyData.viceWeaponAppearanceId) EntityFactory.SetDyeAppearanceList(heroArmyData.viceDyeCurPartList) EntityFactory.DoCreateAdjutantEntity(entity) end entity:SyncFinish() if LevelManager.IsFairLevel() then self:TryAddLvBuFairAddingBuff(entity) end return entity end function SyncLevelFunction:AddHeroRuleBuffs(heroEntity) --子类重写这个方法以添加一些关卡规则相关的Buff end function SyncLevelFunction:OnLoadFinished() Base.OnLoadFinished(self) MultiplayManager.OnLoadFinished() end function SyncLevelFunction:BeginLevel() if self.HadStopLevel then logging.error(LTAG.ERROR, "[gws] : 发现调用 LevelManager.EnterSyncLevel 时 ,levelfunction已经结束了 !") end Base.BeginLevel(self) if not self.isFreeToEnterAndLeave then self:RefreshAllCountryJobBuff() end if self:ShouldWaitOtherPlayer() then self.waitingOtherPlayerMaxFrameDur = self:GetWaitingOtherPlayerMaxFrame() if self.isFreeToEnterAndLeave then if self.shouldCountDownAtFirst then self:ChangeWaitingPhase(ELevelWaitingPhase.COUNT_DOWN) else self:ChangeWaitingPhase(ELevelWaitingPhase.WAITING_OTHER_PLAYER) end else if self.forceWaitOtherPlayerPhase then self:ChangeWaitingPhase(ELevelWaitingPhase.WAITING_OTHER_PLAYER) else --非随进随出关卡是同时创建角色的,所以直接转到Countdown阶段 self:ChangeWaitingPhase(ELevelWaitingPhase.COUNT_DOWN) end end end end function SyncLevelFunction:GetWaitingOtherPlayerMaxFrame() return Const_WaitOthersFrame end function SyncLevelFunction:ShouldWaitOtherPlayer() return self.shouldWaitOtherPlayer end function SyncLevelFunction:ShouldAddCanUseSkillToHeroWhenBorn() --如果需要等待其他玩家,则不在英雄出生时添加可以释放技能的buff return not self:ShouldWaitOtherPlayer() end function SyncLevelFunction:IsWaiting() return self.curWaitingPhase and self.curWaitingPhase ~= ELevelWaitingPhase.WAIT_OVER end function SyncLevelFunction:OnUpdate() if self:ShouldWaitOtherPlayer() then self:UpdateWaiting() end --每30秒统计一下阶段的ping值 if LogicUpdateManager._clientHandleFrame % 600 == 0 and not LogicUpdateManager.IsInReconnectFastPlay() and not LevelManager.IsInLevelWatchBattle() then EventSystem.Dispatch(EventID.BI_LevelMiddle,BIManager.BIType.SyncBattle,"[sync]StagePing",LogicUpdateManager._clientHandleFrame) end end function SyncLevelFunction:DispatchCountdown(second) EventSystem.Dispatch(EventID.UI_SyncSolo_OpenCountDownPanel) EventSystem.Dispatch(EventID.UI_UIBattleCountDownPanel_SetNumber , second) end --战斗刚开始的等待流程,主要是等待其他玩家进入,等待有超时时间 function SyncLevelFunction:UpdateWaiting() if self.curWaitingPhase ~= ELevelWaitingPhase.WAIT_OVER then if self.curWaitingPhase == ELevelWaitingPhase.WAITING_OTHER_PLAYER then if self.curWaitingPhaseFrameEla >= self.waitingOtherPlayerMaxFrameDur then ExpLevelManager.WaitingOtherEnter(true ,LogicUpdateManager._clientHandleFrame ,LogicUpdateManager.GetServerFrame()) self:ChangeWaitingPhase(ELevelWaitingPhase.COUNT_DOWN) elseif LogicUpdateManager.OnlinePlayerCount == LevelPlayerManager.PvpPlayerCount then --人齐了 ExpLevelManager.WaitingOtherEnter(true ,LogicUpdateManager._clientHandleFrame ,LogicUpdateManager.GetServerFrame()) self:ChangeWaitingPhase(ELevelWaitingPhase.COUNT_DOWN) else ExpLevelManager.WaitingOtherEnter(false ,LogicUpdateManager._clientHandleFrame ,LogicUpdateManager.GetServerFrame(), self.waitingOtherPlayerMaxFrameDur) end elseif self.curWaitingPhase == ELevelWaitingPhase.COUNT_DOWN then if self.curWaitingPhaseFrameEla % 20 == 0 then local second = (self.syncWaitingCountDownFrame - self.curWaitingPhaseFrameEla) / 20 self:DispatchCountdown(second) if second <= 0 then EventSystem.Dispatch(EventID.PlayGameSound,"UI_begin") self:ChangeWaitingPhase(ELevelWaitingPhase.WAIT_OVER) end end end self.curWaitingPhaseFrameEla = self.curWaitingPhaseFrameEla + 1 --这个最后加1,因为第0帧也得刷新 end end function SyncLevelFunction:ChangeWaitingPhase(newPhase) if self.curWaitingPhase ~= newPhase then if newPhase == ELevelWaitingPhase.WAITING_OTHER_PLAYER then LevelItemManager.Global:Notify_WaitOtherPlayer_WaitStart() elseif newPhase == ELevelWaitingPhase.COUNT_DOWN then self:DispatchCountdown(self.syncWaitingCountDownFrame / 20) LevelItemManager.Global:Notify_WaitOtherPlayer_CountdownStart() elseif newPhase == ELevelWaitingPhase.WAIT_OVER then self:BeforeWaitFinish(self.curWaitingPhase) LevelItemManager.Global:Notify_WaitOtherPlayer_WaitFinished() end self.curWaitingPhaseFrameEla = 0 self.curWaitingPhase = newPhase else logging.error(LTAG.ERROR, "HYW : 试图重复改变waitingPhase!(无害提示,程序用)") end end function SyncLevelFunction:BeforeWaitFinish(oldPhase) for i,entity in ipairs(LogicUpdateManager.EntityList_Sync) do SystemManager.AddBuff(entity, LuaConst.SpecialBuffs.CanSkill, entity, nil, nil) SystemManager.AddBuff(entity, LuaConst.SpecialBuffs.CanMove, entity, nil, nil) if entity._logicInitData.BattleMemberType == LuaConst.BattleMemberType.Robot_Hero or (not LevelPlayerManager.IsOnline(entity:GetPlayerID()) and not self.dontStartAutoFightWhenPlayerOffline) then SystemManager.StartAutoFight(entity) self.firstOnlineNeedStopAutoFightDic[entity:GetPlayerID()] = true end SystemManager.PausePetAttack(entity, false) -- 宠物可攻击了 end if not USE_LUA_STANDALONE then EventSystem.Dispatch(EventID.Level_CloseUIView, Const_UI_Lua.UIEnum.UIBattleCountDownFormalPanel) end end function SyncLevelFunction:OnFightModeChanged(entity,fightMode) end function SyncLevelFunction:SwitchAutoFight(entity) local robotAI = self:GetRobotAI(entity) if robotAI then entity.components.aiBase:SetSingleCurrentAiByID(robotAI) end entity.components.autoFight:SwitchPvpAutoFightState() end function SyncLevelFunction:StartAutoFight(entity, isSystemLevel, robotAI) if not robotAI then robotAI = self:GetRobotAI(entity) end if robotAI then entity.components.aiBase:SetSingleCurrentAiByID(robotAI) end entity.components.autoFight:StartPvpAutoFight(isSystemLevel) end function SyncLevelFunction:StopAutoFight(entity) entity.components.autoFight:StopPvpAutoFight() end function SyncLevelFunction:OnPlayerFirstOnline(playerID) Base.OnPlayerFirstOnline(self, playerID) if not self.dontStartAutoFightWhenPlayerOffline then if self.firstOnlineNeedStopAutoFightDic[playerID] then local entities = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) if entities then for i,_entity in ipairs(entities) do self:StopAutoFight(_entity) end end self.firstOnlineNeedStopAutoFightDic[playerID] = nil end end end function SyncLevelFunction:OnReconnect(playerID) local entities = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) if entities then for i,_entity in ipairs(entities) do self:StopAutoFight(_entity) end end end --TCP 传过来将要上场人的信息 function SyncLevelFunction:SetNewPlayerInfo(battleRoomRoleInfo) LevelPlayerManager.SetSyncPlayerArmyTable(battleRoomRoleInfo) end --只有随进随出玩法会调这个 function SyncLevelFunction:SetPlayerActive(playerID) --不调用Base,只有联机玩法才需要实现SetPlayerActive local playerHeroArmy = LevelPlayerManager.PlayersArmyTable[playerID] if not playerHeroArmy then logging.error(LTAG.ERROR, "【GWS】严重错误:创建主将时居然没有找到玩家战斗信息 playerID : ", playerID) return end if not self.isFreeToEnterAndLeave then logging.error(LTAG.ERROR, "【GWS】严重错误:同进玩法中 ,不应该有中途创建entity的调用,即不应该有ActiveSelf指令。 playerID : ", playerID) return end self:CreatePlayerEntities(playerID) self:OnPlayerActived(playerID) end --只有随进随出玩法会调这个 function SyncLevelFunction:OnPlayerActived(playerID) LevelPlayerManager.OnPlayerActived(playerID) self:RefreshCountryJobBuff_PlayerActive(playerID) self.exp:OnSetPlayerActive(playerID) end function SyncLevelFunction:RefreshAllFightMode() for playerIDTemp,playerHeroArmy in pairs(LevelPlayerManager.PlayersArmyTable) do --pairs safe SystemManager.SetFightMode时序不敏感 local entityTable = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerIDTemp) if entityTable then local mainEntity = entityTable[1] if mainEntity then --强制刷新 self:RefreshFightModeOfPlayer(playerIDTemp, mainEntity:GetFightMode()) end end end end --强制刷新单位的战斗模式,会连带刷新忽略敌人列表。一般调用都是为了刷新忽略敌人列表 function SyncLevelFunction:RefreshFightModeOfPlayer(playerID, fightMode) SystemManager.SetFightMode(playerID, fightMode, true) end --返回参与击杀时限(可以理解为助攻时限),以帧数表示。 --可以被子类重写,不同关卡可以定义不同的助攻时限 function SyncLevelFunction:GetHeroKillInvolvedTimeLimitFrame() return 600 --抽象基类写死600 end function SyncLevelFunction:HandleHeroDead(deadEntity, killer) if not killer then --好像有些情况关卡会杀Hero,且这种情况下KillerEntity为空,所以这里要判空 return end Base.HandleHeroDead(self, deadEntity, killer) local directKiller = killer --载具击杀则算在载具的驾驶者身上 if killer:IsVehicle() then killer = killer.components.drive:GetLastDriver() end -- 现在策划可直接控制怪物,怪物不是载具但是被驾驶 if killer.components.drive and killer.components.drive:GetDriver() then killer = killer.components.drive:GetDriver() end --召唤物或者副将的击杀算在主将身上 killer = killer:GetMasterEntity() or killer local dStatistic = self.PlayerStatisticDic[deadEntity:GetPlayerID()] local deadArmyPos = LevelPlayerManager.GetHeroArmyPos(deadEntity:GetPlayerID(), deadEntity:GetCharacterID()) or 0 local deadEntityDeadCountKey = EStatisticType.Hero1DeadCount + deadArmyPos dStatistic[deadEntityDeadCountKey] = dStatistic[deadEntityDeadCountKey] + 1 --logging.error(LTAG.ERROR, "HYW :player ", deadEntity:GetPlayerID(), " character ", deadEntity:GetCharacterID(), "重伤次数 : ", dStatistic[deadEntityDeadCountKey]) --logging.error(LTAG.ERROR, "HYW : 玩家 : ", deadEntity._logicInitData.Name, " 阵亡次数+1, 当前阵亡次数 : ", dStatistic[EStatisticType.DeadCount]) local deadComboKill = dStatistic[EStatisticType.CurComboHeroKillNum] or 0 self.exp:BeforeClearHeroComboKillNum(deadEntity, dStatistic[EStatisticType.CurComboHeroKillNum], killer) dStatistic[EStatisticType.CurComboHeroKillNum] = 0 --清空连杀 dStatistic[EStatisticType.RelaxCurComboHeroKillNum] = 0 --清空连杀 local involveKillPlayerIDList = {} --参与击杀的玩家列表,包括击杀者和助攻者 local assistHeroEntityIDList = {} local relaxAssistPlayerIDList = {} local relaxKillPlayerIDList = {} local killerPlayerID = killer:GetPlayerID() local kStatistic = self.PlayerStatisticDic[killerPlayerID] local timeLimit = self:GetHeroKillInvolvedTimeLimitFrame() --刷新击杀者的击杀数、当前连杀数、最大连杀数 if kStatistic and deadEntity ~= killer then --玩家击杀数+1 kStatistic[EStatisticType.HeroKillNum] = kStatistic[EStatisticType.HeroKillNum] + 1 --logging.error(LTAG.ERROR, "HYW : 玩家 : ", killer._logicInitData.Name, " 击杀次数+1, 当前击杀次数 : ", kStatistic[EStatisticType.HeroKillNum]) --实施击杀的英雄的击杀数+1 local heroKillNumKey = EStatisticType.Hero1KillNum + LevelPlayerManager.GetHeroArmyPos(killerPlayerID, killer:GetCharacterID()) kStatistic[heroKillNumKey] = (kStatistic[heroKillNumKey] or 0) + 1 --logging.error(LTAG.ERROR, "HYW : hero ", LevelPlayerManager.GetHeroArmyPos(killerPlayerID, killer:GetCharacterID()) + 1, " 击杀数 + 1, 当前击杀数 : ", kStatistic[heroKillNumKey]) --当前连杀数+1 local curComboHeroKillNum = kStatistic[EStatisticType.CurComboHeroKillNum] + 1 kStatistic[EStatisticType.CurComboHeroKillNum] = curComboHeroKillNum --logging.error(LTAG.ERROR, "HYW : 玩家ID : ", playerID, " 连杀次数+1, 当前连杀次数 : ", kStatistic[EStatisticType.CurComboHeroKillNum]) --如果当前连杀数大于最大连杀数,则刷新最大连杀数 if curComboHeroKillNum > kStatistic[EStatisticType.MaxComboHeroKillNum] then kStatistic[EStatisticType.MaxComboHeroKillNum] = curComboHeroKillNum --logging.error(LTAG.ERROR, "HYW : 玩家ID : ", playerID, " 刷新最大连杀次数 : ", curComboHeroKillNum) end if kStatistic[EStatisticType.FlagIsDriving] and directKiller:IsVehicle() then kStatistic[EStatisticType.CurDrivingComboHeroKillNum] = (kStatistic[EStatisticType.CurDrivingComboHeroKillNum] or 0) + 1 kStatistic[EStatisticType.DrivingHeroKillNum] = (kStatistic[EStatisticType.DrivingHeroKillNum] or 0) + 1 self:RefreshBiggestDrivingComboKillAndAssistNum(kStatistic) end table.insert(involveKillPlayerIDList, killerPlayerID) kStatistic[EStatisticType.InvolvedHeroKillNum] = kStatistic[EStatisticType.InvolvedHeroKillNum] + 1 --else --正常现象,有些单位不会创建statistic,比如小兵,不创建statistic的单位就不统计 end local deadDmgTimestampRecord = self.DmgTimestampRecordDic[deadEntity.EntityID] if deadDmgTimestampRecord then local statisticTemp = nil --刷新相关【玩家】的参与击杀、助攻数 for playerID , frame in pairs(deadDmgTimestampRecord.playerDmgTimestampDic) do -- pairs safe 【注意】这个for循环里只允许刷新数据,不允许做逻辑,因为是pairs ! if LogicUpdateManager._clientHandleFrame - frame <= timeLimit then local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerID) statisticTemp = self.PlayerStatisticDic[playerID] if statisticTemp and playerInfo.campID ~= deadEntity:GetCampID() then if playerID ~= killerPlayerID then --killer 在上面已经加了 InvolvedHeroKillNum 了, 并且 killer 不需要加 AssistHeroKillNum table.insert(involveKillPlayerIDList, playerID) statisticTemp[EStatisticType.InvolvedHeroKillNum] = statisticTemp[EStatisticType.InvolvedHeroKillNum] + 1 statisticTemp[EStatisticType.AssistHeroKillNum] = statisticTemp[EStatisticType.InvolvedHeroKillNum] - statisticTemp[EStatisticType.HeroKillNum] end if statisticTemp[EStatisticType.FlagIsDriving] then statisticTemp[EStatisticType.CurDrivingComboInvolveKillNum] = (statisticTemp[EStatisticType.CurDrivingComboInvolveKillNum] or 0) + 1 self:RefreshBiggestDrivingComboKillAndAssistNum(statisticTemp) end --logging.error(LTAG.ERROR, "HYW : 玩家ID : ", playerID, " 参数击杀次数+1, 当前参数击杀次数 : ", statisticTemp[EStatisticType.InvolvedHeroKillNum], " 助攻次数 : ", statisticTemp[EStatisticType.AssistHeroKillNum]) if self.relaxKillConditionFrame then if LogicUpdateManager._clientHandleFrame - frame <= self.relaxKillConditionFrame then -- 放宽击杀限制时,将部分助攻数据放入到击杀数据中 statisticTemp[EStatisticType.RelaxHeroKillNum] = (statisticTemp[EStatisticType.RelaxHeroKillNum] or 0) + 1 local curMainEntity = LevelPlayerManager.GetPlayerActiveEntity(playerID) local killNumKey = EStatisticType.RelaxHero1KillNum + LevelPlayerManager.GetHeroArmyPos(playerID, curMainEntity:GetCharacterID()) statisticTemp[killNumKey] = statisticTemp[killNumKey] + 1 local relaxCurComboHeroKillNum = statisticTemp[EStatisticType.RelaxCurComboHeroKillNum] + 1 statisticTemp[EStatisticType.RelaxCurComboHeroKillNum] = relaxCurComboHeroKillNum if relaxCurComboHeroKillNum > statisticTemp[EStatisticType.RelaxMaxComboHeroKillNum] then -- --如果当前连杀数大于最大连杀数,则刷新最大连杀数 statisticTemp[EStatisticType.RelaxMaxComboHeroKillNum] = relaxCurComboHeroKillNum end if statisticTemp[EStatisticType.FlagIsDriving] then statisticTemp[EStatisticType.RelaxDrivingHeroKillNum] = (statisticTemp[EStatisticType.RelaxDrivingHeroKillNum] or 0) + 1 end table.insert(relaxKillPlayerIDList,playerID) else local assistCurEntity = LevelPlayerManager.GetPlayerActiveEntity(playerID) local assistKey = EStatisticType.RelaxHero1AssistHeroKillNum + LevelPlayerManager.GetHeroArmyPos(playerID, assistCurEntity:GetCharacterID()) statisticTemp[assistKey] = statisticTemp[assistKey] + 1 statisticTemp[EStatisticType.RelaxAssistHeroKillNum] = (statisticTemp[EStatisticType.RelaxAssistHeroKillNum] or 0) + 1 table.insert(relaxAssistPlayerIDList,playerID) end end end end end table.sort(involveKillPlayerIDList, SimpleSorter) --必须排序,因为是由 pairs 收集到的数据 for i,playerID in ipairs(involveKillPlayerIDList) do if playerID ~= killerPlayerID then self:OnPlayerAssistAdd(playerID, statisticTemp) --参与击杀的玩家中,不为击杀者的,助攻增加 end end --刷新相关【单位】的参与击杀数、助攻数 for entityID,frame in pairs(deadDmgTimestampRecord.heroDmgTimestampDic) do -- pairs safe 【注意】这个for循环里只允许刷新数据,不允许做逻辑,因为是pairs ! if LogicUpdateManager._clientHandleFrame - frame <= timeLimit then table.insert(assistHeroEntityIDList, entityID) local entity = LogicUpdateManager._entityDic[entityID] local playerID = entity:GetPlayerID() statisticTemp = self.PlayerStatisticDic[playerID] local armyPos = LevelPlayerManager.GetHeroArmyPos(playerID, entity:GetCharacterID()) -- armyPos是从0-5的 --参与击杀数+1 local heroInvolvedKillKey = EStatisticType.Hero1InvolvedKillNum + armyPos statisticTemp[heroInvolvedKillKey] = statisticTemp[heroInvolvedKillKey] + 1 local heroAssistKillKey = EStatisticType.Hero1AssistHeroKillNum + armyPos local heroKillNumKey = EStatisticType.Hero1KillNum + armyPos statisticTemp[heroAssistKillKey] = statisticTemp[heroInvolvedKillKey] - statisticTemp[heroKillNumKey] --logging.error(LTAG.ERROR, "HYW : 英雄CharacterID : ", entity:GetCharacterID(), " 助攻数 : ", statisticTemp[heroAssistKillKey], " 击杀数 : ", statisticTemp[heroKillNumKey]) end end table.sort(assistHeroEntityIDList, SimpleSorter) --必须排序,因为是由 pairs 收集到的数据 for i,entityID in ipairs(assistHeroEntityIDList) do if entityID ~= killer.EntityID then --不是击杀者的单位,助攻增加 self:OnHeroAssistNumAdd(entityID) end end --计算完之后,清空deadDmgTimestampRecord(Hero死亡、算完击杀助攻后清空时间戳) table.clear(deadDmgTimestampRecord.playerDmgTimestampDic) table.clear(deadDmgTimestampRecord.heroDmgTimestampDic) --else --正常情况,比如killer是小兵,且deadEntity没被Hero打过,此时deadDmgTimestampRecord就为空,因为小兵伤害不会放在DmgTimestampRecordDic里 end --处理复仇↓ local killerLastKilledByHeroRecord = killer.components.record.lastKilledByHeroRecord if killerLastKilledByHeroRecord then if killerLastKilledByHeroRecord.killer:GetPlayerID() == deadEntity:GetPlayerID() and deadEntity:IsHero() then self:OnRevenge(killer, deadEntity, killerLastKilledByHeroRecord) end end --处理复仇↑ if killer:IsHero() then self:OnHeroKillHero(killer, deadEntity, deadComboKill) end self.exp:OnHandleHeroDead(killer, deadEntity, involveKillPlayerIDList, assistHeroEntityIDList, kStatistic, relaxAssistPlayerIDList, relaxKillPlayerIDList) self:AfterHanleHeroDead(killer, deadEntity, involveKillPlayerIDList, assistHeroEntityIDList, kStatistic) end function SyncLevelFunction:AfterHanleHeroDead(killer, deadEntity, involveKillPlayerIDList, assistHeroEntityIDList, kStatistic) end function SyncLevelFunction:RefreshBiggestDrivingComboKillAndAssistNum(statistic) local comboInvolveKillNum = statistic[EStatisticType.CurDrivingComboInvolveKillNum] or 0 if comboInvolveKillNum > 0 then if not statistic[EStatisticType.BiggestDrivingComboKillAndAssistNum] or statistic[EStatisticType.BiggestDrivingComboKillAndAssistNum] < comboInvolveKillNum then statistic[EStatisticType.BiggestDrivingComboKillAndAssistNum] = comboInvolveKillNum end end end function SyncLevelFunction:OnRevenge(killer, deadEntity, killerLastKilledByHeroRecord) local kStatistic = self.PlayerStatisticDic[killer:GetPlayerID()] if kStatistic then kStatistic[EStatisticType.RevengeCount] = (kStatistic[EStatisticType.RevengeCount] or 0) + 1 local frameInterval = LogicUpdateManager._clientHandleFrame - killerLastKilledByHeroRecord.frame if kStatistic[EStatisticType.MinRevengeInterval] == nil or kStatistic[EStatisticType.MinRevengeInterval] > frameInterval then kStatistic[EStatisticType.MinRevengeInterval] = frameInterval end end end function SyncLevelFunction:OnPlayerAssistAdd(playerID, playerStatistic) --子类可以重写,基于数据自己做一些统计 end function SyncLevelFunction:OnHeroAssistNumAdd(entityID) --子类可以重写,基于数据自己做一些统计 end function SyncLevelFunction:OnOffDrive(driver, beDrivedEntity) Base.OnOffDrive(self, driver, beDrivedEntity) --下载具时刷新最大载具连续【击杀+助攻】数 if driver then local playerID = driver:GetPlayerID() local statistic = self.PlayerStatisticDic[playerID] if statistic then statistic[EStatisticType.CurDrivingComboInvolveKillNum] = 0 statistic[EStatisticType.CurDrivingComboHeroKillNum] = 0 else logging.error(LTAG.ERROR, "HYW : statistic nil Error @SyncLevelFunction:OnOffDrive") end else logging.error(LTAG.ERROR, "HYW : driver nil Error @SyncLevelFunction:OnOffDrive") end end function SyncLevelFunction:OnHeroBorn(entity) Base.OnHeroBorn(self, entity) self:AddHeroRuleBuffs(entity) end -- --这里的OnHeroDead可以删掉了,挪到了LevelFunction -- function SyncLevelFunction:OnHeroDead(entity) -- self:HandleHeroDead(entity, entity.components.record.KillerEntity) -- end function SyncLevelFunction:OnEntityBorn(entity) Base.OnEntityBorn(self, entity) for i,record in ipairs(self.addBuffServerActionRecords) do private.TryAddBuffsToEntityByRecord(entity, record) end end function SyncLevelFunction:OnVehicleDead(entity) Base.OnVehicleDead(entity) --统计驾驶者从载具上被打下来的次数↓↓↓↓ local driver = entity.components.drive.BeDrivedByEntity if driver and driver:IsHero() then local playerID = driver:GetPlayerID() local statistic = self.PlayerStatisticDic[playerID] if statistic then statistic[EStatisticType.KickFromVehicleCount] = (statistic[EStatisticType.KickFromVehicleCount] or 0) + 1 end end --统计驾驶者从载具上被打下来的次数↑↑↑↑ end function SyncLevelFunction:GetReviveTime() return 10 * 1000 end function SyncLevelFunction:GetHeroAiDelayFrame() return 90 end function SyncLevelFunction:GetPlayerKillNum(playerID) if self.PlayerStatisticDic[playerID] then return self.PlayerStatisticDic[playerID][EStatisticType.HeroKillNum] else return 0 end end --获取玩家当前连杀数 function SyncLevelFunction:GetPlayerComboKillNum(playerID) if self.PlayerStatisticDic[playerID] then return self.PlayerStatisticDic[playerID][EStatisticType.CurComboHeroKillNum] else return 0 end end function SyncLevelFunction:GetHeroKillNum(playerID, heroPos) if self.PlayerStatisticDic[playerID] then return self.PlayerStatisticDic[playerID][EStatisticType.Hero1KillNum + heroPos - 1] or 0 else return 0 end end --获取玩家参与击杀的数量(击杀+助攻) function SyncLevelFunction:GetPlayerInvolvedKillNum(playerID) if self.PlayerStatisticDic[playerID] then return self.PlayerStatisticDic[playerID][EStatisticType.InvolvedHeroKillNum] else return 0 end end --获取玩家助攻数 function SyncLevelFunction:GetPlayerAssistKillNum(playerID) if self.PlayerStatisticDic[playerID] then return self.PlayerStatisticDic[playerID][EStatisticType.AssistHeroKillNum] else return 0 end end function SyncLevelFunction:GetServerBattleActionRequestList() return self.serverBattleActionRequestList end function SyncLevelFunction:AddServerBattleActionRequest(actionID) table.insert(self.serverBattleActionRequestList, actionID) end --如果要统计助攻数,子类在重写OnHpChange时一定要调用Base,否则可以不调用 function SyncLevelFunction:OnHpChange(entity,caster,delta,op) if op == "sub" and delta < FP.Const.Zero then -- 先不考虑加血的情况 ,如果有 ,需要改下面代码 -- 对boss、精英、人的伤害时间戳统计,是为了做助攻数统计 if entity:IsBoss() or entity:IsHero() or entity:IsElite()then local dmgDealer = caster:GetOriginEntity() if dmgDealer:IsHero() then self:RefreshDamageTimestampRecord(entity.EntityID, dmgDealer) if self.needRecordPlayerToBossDamage then if entity:IsBoss() then self:AddPlayerToBossDamageRecord(dmgDealer, delta) end end end end end end function SyncLevelFunction:AddPlayerToBossDamageRecord(dmgDealer, delta) local playerID = dmgDealer:GetPlayerID() self.playerToBossDmgRecord[self.playerToBossDmgRecordPointerPos + 1] = playerID self.playerToBossDmgRecord[self.playerToBossDmgRecordPointerPos + 2] = -delta[1] self.playerToBossDmgRecord[self.playerToBossDmgRecordPointerPos + 3] = LogicUpdateManager._clientHandleFrame local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerID) self.playerToBossDmgRecord[self.playerToBossDmgRecordPointerPos + 4] = playerInfo and playerInfo.acid or "nil" self.playerToBossDmgRecordPointerPos = self.playerToBossDmgRecordPointerPos + 4 if self.playerToBossDmgRecordPointerPos >= self.playerToBossDmgRecordMaxLength then self.playerToBossDmgRecordPointerPos = self.playerToBossDmgRecordPointerPos - self.playerToBossDmgRecordMaxLength + 1 if self.playerToBossDmgRecordPointerPos > self.playerToBossDmgRecordMaxLength then logging.error(LTAG.ERROR, "HYW : self.playerToBossDmgRecordPointerPos > self.playerToBossDmgRecordMaxLength Error! value : ", self.playerToBossDmgRecordPointerPos) end end --logging.error(LTAG.ERROR, "HYW : self.playerToBossDmgRecordPointerPos : ", self.playerToBossDmgRecordPointerPos, " #self.playerToBossDmgRecord : ", #self.playerToBossDmgRecord) --self.playerToBossDmgRecord[1] = self.playerToBossDmgRecordPointerPos --这里不刷新,调用GetPlayerToBossDamageRecord的时候再刷新以节省性能 end function SyncLevelFunction:GetPlayerToBossDamageRecord() self.playerToBossDmgRecord[1] = self.playerToBossDmgRecordPointerPos return self.playerToBossDmgRecord end function SyncLevelFunction:RefreshDamageTimestampRecord(beHurtEntityID, dmgDealer) local record = self.DmgTimestampRecordDic[beHurtEntityID] if record then record.playerDmgTimestampDic[dmgDealer:GetPlayerID()] = LogicUpdateManager._clientHandleFrame record.heroDmgTimestampDic[dmgDealer.EntityID] = LogicUpdateManager._clientHandleFrame else self:CreateDamageTimestampRecord(beHurtEntityID, dmgDealer) --logging.error(LTAG.ERROR, "HYW : self.DmgTimestampRecordDic add record , beHurtEntityID : ", beHurtEntityID, " dealerPlayer : ", dmgDealer:GetPlayerID(), " dealerEntity : ", dmgDealer.EntityID) end end function SyncLevelFunction:CreateDamageTimestampRecord(beHurtEntityID, dmgDealer) local record = {} record = { --策划要求对于玩家和单位都要分别进行击杀/助攻统计,所以记录玩家伤害时间戳的playerDmgTimestampDic、记录单位伤害时间戳的heroDmgTimestampDic都要有 playerDmgTimestampDic = {[dmgDealer:GetPlayerID()] = LogicUpdateManager._clientHandleFrame}, --key是playerID,value是这个玩家造成伤害的时间戳 heroDmgTimestampDic = {[dmgDealer.EntityID] = LogicUpdateManager._clientHandleFrame}, --key是entityID,value是这个单位造成伤害的时间戳 } self.DmgTimestampRecordDic[beHurtEntityID] = record return record end function SyncLevelFunction:OnPlayerAllHeroDead(entity) --不调用Base 联机玩法所有英雄死亡时一般跟单机玩法不同,所以统一不调用Base local heroEntities = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(entity._logicInitData.PlayerIDInRoom) if heroEntities then for i,heroEntity in ipairs(heroEntities) do entity.components.revive.ReviveTimer = self:GetReviveTime() end end end function SyncLevelFunction:ReviveTimeIsUp(entity) entity.components.record.PassivitySpecialCommand = LuaConst.SpecialCommandType.ReviveNeerby_Passive-- 倒计时结束,出生点复活 end function SyncLevelFunction:StopLevel(isClickExitBtn) --如果在断线重连,追帧的过程中 ,战斗结束了 ,此时loading是打开状态,玩家没法出去了,需要执行追帧结束。 if LogicUpdateManager.IsInReconnectFastPlay() then EventSystem.Dispatch(EventID.BI_LevelMiddle,BIManager.BIType.SyncBattle,"[sync]InFastPlayStopLevel") SystemManager.OnFastPlayOver() if LogicUpdateManager._curEntity == nil then -- 当在快速补帧(loading)时,战斗结束了,但是自己还没创建出来,防止卡住,需要退出关卡。 EventSystem.Dispatch(EventID.Level_SendRoleLeaveFuben,true) end end if not LevelManager.IsReplayBattle() then EventSystem.Dispatch(EventID.BI_LevelMiddle,BIManager.BIType.SyncBattle,"[sync]StopLevel") end if not LogicUpdateManager.IsReplayOrServerCheck then MultiplayManager.CloseKCP() --关的KCP 并 清理kcpTransPort中的缓存数据 else KCPTransport.ClearKCPData() -- 清理kcpTransPort中的缓存数据 -- CompareSyncLogManager.TryColectLog(LuaConst.CheckBattleLogType.CollectEid2HeroID) BattleLogSetter.ForceWriteLog() end BattleSimulator.Client_SaveReplayData(false) SaveSyncBattleInfoManager.WriteToFile() LogicUpdateManager.SetServerReplenishFrame(-1) EventSystem.Dispatch(EventID.JoinBattleTeamChatChannel ,MultiplayManager.GetVerifyRoomID() ,false) -- 退出聊天室 Base.StopLevel(self) self.HadStopLevel = true MultiplayManager.SetVerifyRoomID(nil) MultiplayManager.SetServerID(nil) if LevelManager.IsInLevelWatchBattle() then WatchBattleManager.OnLevelStop(self.watchCheckStr, isClickExitBtn) end self.exp:OnLevelOver() end function SyncLevelFunction:GetBattleResultCheckStr() local result = "-" -- 服务器认为""有特殊含义 for i,entity in ipairs(LogicUpdateManager.EntityList_Sync) do result = result .. self:GetBattleResultCheckStrOfEntity(entity) end return result end function SyncLevelFunction:OnLevelSucceed() -- 不能用base.OnLevelSucceed,因为同步玩法没有结束就播放三星胜利界面的 self:StopLevel() if LogicUpdateManager.IsReplayOrServerCheck then -- 客户端重播或服务器校验 BattleSimulator.EndPlay() end end function SyncLevelFunction:OnLevelFail() -- 不能用base.OnLevelFail,因为同步玩法没有结束就播放三星胜利界面的 self:StopLevel() if LogicUpdateManager.IsReplayOrServerCheck then -- 客户端重播或服务器校验的话,不要发送TCP BattleSimulator.EndPlay() end end function SyncLevelFunction:QuitRoom(entity) if self.isFreeToEnterAndLeave and not self.retainWhenQuitRoom or self.forceQuitRoomAndNotRetain then --不调用base self:QuitRoomAndNotRetain(entity) else self:OffLineAndRetain(entity:GetPlayerID()) end if entity == LogicUpdateManager._curEntity then if not LogicUpdateManager.IsReplayOrServerCheck and not LevelManager.IsInLevelWatchBattle() and not LevelManager.IsReplayBattle() then --客户端重播或服务器校验或观战 的话,不要执行下面代码 EventSystem.Dispatch(EventID.Level_SendRoleLeaveFuben ,true) self:StopLevel() end end end function SyncLevelFunction:QuitRoomAndNotRetain(entity) SystemManager.Background(entity) self:RefreshCountryJobBuff_PlayerQuit(entity:GetPlayerID()) LogicUpdateManager.RemoveSyncLevelEntity(entity:GetPlayerID()) end --OffLineAndRetain :QuitRoom后保留在战斗内。非随紧随出玩法默认都会保留,比如组队副本。玩家退出后会保留在战斗内。 function SyncLevelFunction:OffLineAndRetain(playerID) --一个玩家的每个Entity都会 OffLineAndRetain,所以这里不能GetPlayerEntitysByPlayerIDInRoom local entities = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) -- for i,entity in ipairs(entities) do -- logging.error(LTAG.ERROR, "HYW : add : " .. tostring(entity.EntityID)) -- table.insert(self.RobotList,entity) -- end if entities == nil then logging.error(LTAG.ERROR, "GWS :奇怪现象! OffLineAndRetain ,cant find player entities, playerID = " , playerID) return end if not self.dontStartAutoFightWhenRetain then -- self.dontStartAutoFightWhenRetain : 是否在OffLineAndRetain时开启自动战斗。默认都会开启 SystemManager.StartAutoFight(entities[1]) end table.insert(self.RobotList,entities[1]) end -- 获取所有玩家对boss的伤害统计 (只给服务器使用的代码),要使用这部分代码 ,需要在自己的fuction中统计 local temp_dmgDic = {} function SyncLevelFunction:GetBossDamageRecord() table.clear(temp_dmgDic) 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 damage = math.floor( FP.tonumberV1(statisticInfo[EStatisticType.DamageBossHP]) ) local playerName = playerInfo.name if temp_dmgDic[acid] then -- 这个玩家退出后又进来玩了,前后伤害算在一起 temp_dmgDic[acid] = temp_dmgDic[acid] + damage damage = temp_dmgDic[acid] else temp_dmgDic[acid] = damage end table.insert(info, acid) table.insert(info, guildID) table.insert(info, damage) table.insert(info, playerName) end end return info end function SyncLevelFunction:TryAddDamageBossHPStatistic(hpChangeEntity, caster, delta, op) if hpChangeEntity:IsBoss() then local sourceEntity = nil if delta < FP.Const.Zero and op == "sub" and caster then sourceEntity = caster:GetOriginEntity() end if sourceEntity then local playerRoomID = sourceEntity:GetPlayerID() if playerRoomID > 0 then self.PlayerStatisticDic[playerRoomID][EStatisticType.DamageBossHP]:Sub(delta) end end end end function SyncLevelFunction:OnPlaySyncTimeline() self.syncTimelineIndex = self.syncTimelineIndex + 1 end function SyncLevelFunction:GetSyncTimelineIndex() return self.syncTimelineIndex end ---- ↓↓↓↓ ServerBattleAction(服务器发起的战斗行为) Start ↓↓↓↓ ---------------------------- function private.GetServerBattleActionId(command) local high = math.floor(FP.tonumberV1(command.SkillJoystickPosX) * 100 + 0.5) --跟服务器定死的规则,command.SkillJoystickPosX这个short代表int值的高位,SkillJoystickPosZ代表int值的低位 local low = math.floor(FP.tonumberV1(command.SkillJoystickPosZ) * 100 + 0.5) --服务器发过来的正数int值高位数据在客户端的c#会被转为short,大于32767的会变为负数,这里得转换为ushort的正数 if high < 0 then high = 65536 + high end if low < 0 then low = 65536 + low end return high * 65536 + low end function SyncLevelFunction:HandleServerAction(entity ,command) local actionID = private.GetServerBattleActionId(command) self:DoServerActionByID(entity, actionID, command) end function SyncLevelFunction:DoServerActionByID(entity, actionID, command) local EServerBattleActionType = LuaConst.EServerBattleActionType local actionData = DataSheetManager.ServerBattleAction:GetDataByID(actionID) local result = true --logging.error(LTAG.ERROR, "HYW : HandleServerAction : ", actionID, " frame : ", LogicUpdateManager._clientHandleFrame) if actionData then local actionType = actionData.BattleActionType --此处可以考虑用dic优化 if actionType == EServerBattleActionType.ADD_BUFF then result = self:ServerActionAddBuff(entity, actionData, command) elseif actionType == EServerBattleActionType.CHANGE_RESOURCE_AMOUNT then --改变资源数量 result = private.ServerActionCutResourceAmount(entity, actionData, command) elseif actionType == EServerBattleActionType.COMBINATION then --组合行为 for i,_actionID in ipairs(actionData.IntParamList) do result = self:DoServerActionByID(entity, _actionID, command) if result == false then -- 这里必须 == false才跳出,== nil不跳出 logging.error(LTAG.ERROR, "HYW : 战斗程序、战斗策划注意! DoServerActionByID 失败 提前跳出 ServerBattleAction ID :", _actionID) break end end elseif actionType == EServerBattleActionType.LEVEL_FINISH then result = private.ServerActionLevelFinish(entity, actionData, command) elseif actionType == EServerBattleActionType.CREATE_MONSTER then result = private.ServerActionCreateMonster(entity, actionData, command) elseif private.IsCheatServerAction(actionType) then if LevelManager.IsMonsterTest() then if actionType == EServerBattleActionType.CREATE_TEST_MONSTER then result = private.ServerActionCreateTestMonster(entity, actionData, command) elseif actionType == EServerBattleActionType.TEST_MONSTER_USE_SELF_AI then result = private.ServerActionSetTestMonsterUseSelfAI(entity, actionData, command) elseif actionType == EServerBattleActionType.CONTROLLED_AI_USE_SKILL then result = private.ServerActionControlledAIUseSkill(entity, actionData, command) else logging.error(LTAG.ERROR, "HYW : 未实现的 Cheat ServerBattleActionType : ", actionType) end else logging.error(LTAG.ERROR, "HYW : 策划程序注意!试图在非联机测试关调用Cheat插帧!这是禁止的!") end else logging.error(LTAG.ERROR, "HYW : 未实现的ServerBattleActionType : ", actionType) end else logging.error(LTAG.ERROR, "HYW : HandleServerAction 错误!actionData 为空。 actionID : ", actionID) end return result end function private.ServerActionCutResourceAmount(entity, actionData, command) return LevelManager.CurLevelFunc:ChangeResourceAmountOfCamp(actionData.IntParam1, -actionData.IntParamList[1]) end local EServerAddBuffTargetType = { --负数段表示MonsterType MONSTER_TYPE_MAX = -1, FIRST_SYNC_ENTITY = 0, ALL_HERO = 1, ALL_BOSS = 2, MONSTER_OF_LEVELITEM = 3, --↓↓↓这一段的都弃用了,可以删了。(阵营信息放到IntParam3里去了) CAMP_HERO_START = 100, CAMP_HERO_1 = 101, CAMP_HERO_2 = 102, -- ... 依此类推,这中间的枚举名称都不重要 CAMP_HERO_END = 199, --↑↑↑↑这一段的都弃用了,可以删了 } local ADD_BUFF_INSTANTLY = 2 --表示添加buff的行为是瞬时,不会给将来出生的目标添加buff。反之则会给将来的目标添加buff function SyncLevelFunction:ServerActionAddBuff(entity, actionData, command) local layer = math.max(1, command.SkillIndex) --这里的参数解析规则在proto工程的《服务器插帧说明文档.xlsx》里有 local duration = command.MoveType * 256 + command.DirAngle if entity then --如果存在entity,表示给单独一个单位添加buff local record = { playerID = entity:GetPlayerID(), actionData = actionData, layer = layer, buffList = actionData.IntParamList, targetType = actionData.IntParam1, duration = duration, } for i,_entity in ipairs(LogicUpdateManager.EntityList_Sync) do if _entity:IsHero() and _entity:GetPlayerID() == record.playerID then private.AddBuffsToEntity(_entity, actionData.IntParamList, layer, duration) end end table.insert(self.addBuffServerActionRecords, record) else --如果entity为空,表示按照类型添加buff,会给一类entity添加buff --logging.error(LTAG.ERROR, "HYW : #LogicUpdateManager.EntityList_Sync : ", #LogicUpdateManager.EntityList_Sync) --logging.error(LTAG.ERROR, "HYW : #LogicUpdateManager.EntityList_Normal : ", #LogicUpdateManager.EntityList_Normal) local intParam1 = actionData.IntParam1 local intParam2 = actionData.IntParam2 local intParam3 = actionData.IntParam3 local record = nil if intParam2 ~= ADD_BUFF_INSTANTLY then --表示会给将来出生的目标添加buff,该添加buff的行为是持续性的 record = { layer = layer, buffList = actionData.IntParamList, targetType = intParam1, campID = intParam3, duration = duration, --campID = nil, --可选,指定目标的阵营,未指定则代表所有阵营 --monsterType = nil, --可选,指定怪物的monsterType } --logging.error(LTAG.ERROR, "HYW : 持续添加buff") --else --logging.error(LTAG.ERROR, "HYW : 即时添加buff") end if intParam1 == EServerAddBuffTargetType.FIRST_SYNC_ENTITY then if LogicUpdateManager.EntityList_Sync[1] then private.AddBuffsToEntity(LogicUpdateManager.EntityList_Sync[1], actionData.IntParamList, layer, duration) end elseif intParam1 == EServerAddBuffTargetType.ALL_HERO then --logging.error(LTAG.ERROR, "HYW : 给所有英雄") for i,entity in ipairs(LogicUpdateManager.EntityList_Sync) do if entity:IsHero() and private.MatchCamp(entity, intParam3) then private.AddBuffsToEntity(entity, actionData.IntParamList, layer, duration) end end elseif intParam1 == EServerAddBuffTargetType.ALL_BOSS then --logging.error(LTAG.ERROR, "HYW : 给所有Boss") for i,entity in ipairs(LogicUpdateManager.EntityList_Normal) do if entity:IsBoss() and private.MatchCamp(entity, intParam3) then private.AddBuffsToEntity(entity, actionData.IntParamList, layer, duration) end end elseif intParam1 > EServerAddBuffTargetType.CAMP_HERO_START and intParam1 <= EServerAddBuffTargetType.CAMP_HERO_END then --101代表给campID为1的英雄加、102代表给campID为2的英雄加,依次类推 local campID = intParam1 - EServerAddBuffTargetType.CAMP_HERO_START if record then record.campID = campID end --logging.error(LTAG.ERROR, "HYW : 给阵营", campID, " 英雄") local targets = CampManager.GetEntityListOfCampId(campID) if targets then for j,entity in ipairs(targets) do if entity:IsHero() then private.AddBuffsToEntity(entity, actionData.IntParamList, layer, duration) end end end elseif intParam1 == EServerAddBuffTargetType.MONSTER_OF_LEVELITEM then --给指定ID的怪物关卡元素生成的怪物添加buff,要求这个关卡元素只有1个怪物,否则可能会出现不一致 local levelItem = LevelItemManager.GetItemByID(intParam3, LuaConst.LevelItemType.Monster) if levelItem then local count = 0 for i,v in ipairs(levelItem._allEntityDict) do count = count + 1 if count >= 2 then logging.error(LTAG.ERROR, "HYW : 严重警告! 试图给有多个怪物的怪物元素添加buff,可能导致不一致") break end end local entity = levelItem:GetMonsterEntity() if entity then private.AddBuffsToEntity(entity, actionData.IntParamList, layer, duration) end end elseif intParam1 < 0 then --参数为负数表示给指定monsterType的怪物单位添加buff local monsterType = -intParam1 if record then record.monsterType = monsterType end local targets = TargetManager.GetMonsterListByMonsterType(monsterType) for i,entity in ipairs(targets) do if private.MatchCamp(entity, intParam3) then private.AddBuffsToEntity(entity, actionData.IntParamList, layer, duration) end end --logging.error(LTAG.ERROR, "HYW : 给monsterType:", monsterType, " 单位数量 : ", #targets) else logging.error(LTAG.ERROR, "HYW : ServerBattleAction 添加buff的intParam1值配置异常,无法指定buff的添加目标。 ServerBattleAction ID : ", actionData.ID, " intParam1异常值为 : ", intParam1) end if record then table.insert(self.addBuffServerActionRecords, record) end end return true end function private.TryAddBuffsToEntityByRecord(entity, record) local shouldAdd = false repeat if record.playerID ~= nil and record.playerID ~= entity:GetPlayerID() then --playerID判定未通过 break end if not private.MatchCamp(entity, record.campID) then break end if not private.MatchTargetType(entity, record.targetType, record.monsterType) then break end shouldAdd = true until true if shouldAdd then private.AddBuffsToEntity(entity, record.buffList, record.layer, record.duration) end return shouldAdd end function private.MatchCamp(entity, campID) if campID ~= nil and campID > 0 and campID ~= entity:GetCampID() then return false end return true end function private.MatchTargetType(entity, targetType, monsterType) local isMatch = false if targetType == EServerAddBuffTargetType.ALL_HERO then isMatch = entity:IsHero() elseif targetType == EServerAddBuffTargetType.ALL_BOSS then isMatch = entity:IsBoss() elseif targetType > EServerAddBuffTargetType.CAMP_HERO_START and targetType <= EServerAddBuffTargetType.CAMP_HERO_END then isMatch = entity:IsHero() elseif targetType <= EServerAddBuffTargetType.MONSTER_TYPE_MAX then isMatch = entity:IsMonster() and entity:GetFeatureID() == monsterType end return isMatch end function private.AddBuffsToEntity(entity, buffList, layer, duration) if entity:IsBorned() then --新增判断,要求entity born之后才能添加buff,因为entity在born的时候会根据record尝试添加一次,所以没有born的单位就不需要添加了 for i,buffId in ipairs(buffList) do for i=1,layer do local buffCtrl if duration and duration > 0 then buffCtrl = RuntimeDataManager.New_BuffCtrl() buffCtrl.ForceDuration:Reset(duration) end SystemManager.AddBuff(entity, buffId, entity, nil, buffCtrl) end end end end function private.ServerActionCreateMonster(entity, actionData) return SystemManager.CreateMonster(actionData.IntParam1, actionData.IntParamList[1], actionData.IntParamList[2]) end function SyncLevelFunction:GetHadStopLevel() return self.HadStopLevel end function SyncLevelFunction:TryCreatePlayerStatistic(playerID) --这个方法没有Base了 Base.TryCreatePlayerStatistic(self,playerID) -- 仇敌关系统计 if self.RivalInfoDic[playerID] == nil then local rivalDic = {} local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerID) local rivalInfos = playerInfo.rivalPlayerList for _,info in ipairs(rivalInfos) do rivalDic[info.ACID] = info.IsSwornRival and LuaConst.RivalType.Sworn or LuaConst.RivalType.Common end self.RivalInfoDic[playerID] = rivalDic end end function SyncLevelFunction:ChangeRival(source, target, rivalType) -- 仇敌关系改变 local rivalResult = nil if rivalType == LuaConst.RivalType.Sworn or rivalType == LuaConst.RivalType.Common then rivalResult = rivalType end local targetPlayerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(target) local targetAcid = targetPlayerInfo.acid local rivalDic = self.RivalInfoDic[source] if rivalDic then rivalDic[targetAcid] = rivalResult else rivalDic = {} rivalDic[targetAcid] = rivalResult self.RivalInfoDic[source] = rivalDic end self.exp:OnChangeRival(source, target, rivalResult) end function SyncLevelFunction:GetRivalType(playerID, checkPlayerAcid) -- 获取仇敌关系 local rivalDic = self.RivalInfoDic[playerID] if rivalDic then return rivalDic[checkPlayerAcid] or LuaConst.RivalType.None end return LuaConst.RivalType.None end function SyncLevelFunction:OnHeroKillHero(killer, deadEntity, deadComboKill) EventSystem.Dispatch(EventID.Level_HeroKillHero, killer, deadEntity, deadComboKill) end --↓视野机制--------------------------------------------------------------------------- function SyncLevelFunction:CanSeeEntity(seeingEntity, beSawEntity) local result = Base.CanSeeEntity(self, seeingEntity, beSawEntity) --基类只根据同阵营的视野来判断是否可见 if not result then -- 如果基类的判断看不见,则表示仅依赖阵营视野是看不见的,此时需要判断军团视野是否能看见 local originEntity = seeingEntity:GetOriginEntity() local beSawEntityVisualFieldComp = beSawEntity.components.visualField local beSawEntityVisibilityLv = beSawEntityVisualFieldComp:GetVisiblityLevel() local beSawEntityVisibilityGroup = beSawEntityVisualFieldComp:GetVisiblityGroup() if originEntity:HasGuild() then local guildID = originEntity:GetGuildID() local guildVisualList = self.guildVisualDic[guildID] if guildVisualList then for i,v in ipairs(guildVisualList) do result = seeingEntity.components.visualField.CanSee(v.visualLevel, v.visualGroup, beSawEntityVisibilityLv, beSawEntityVisibilityGroup) if result then break end end end end end return result end function SyncLevelFunction:OnVisualFieldChanged(entity, oldLevel, oldGroup) Base.OnVisualFieldChanged(self, entity, oldLevel, oldGroup) local visualFieldComp = entity.components.visualField if not self.sameGuildCanBeEnemy then local entity = visualFieldComp.entity local playerID = entity:GetPlayerID() local entityID = entity.EntityID local originEntity = entity:GetOriginEntity() if originEntity:HasGuild() then local isListChanged = false local guildID = originEntity:GetGuildID() local guildVisualList = self.guildVisualDic[guildID] if not guildVisualList then guildVisualList = {} self.guildVisualDic[guildID] = guildVisualList end if visualFieldComp:GetVisualLevel() > 0 then local visualInfo for i,v in ipairs(guildVisualList) do if playerID > 0 then if v.playerID == playerID then visualInfo = v break end else if v.entityID == entityID then visualInfo = v break end end end if not visualInfo then visualInfo = {} visualInfo.playerID = playerID visualInfo.entityID = entityID isListChanged = true table.insert(guildVisualList, visualInfo) end visualInfo.visualLevel = visualFieldComp:GetVisualLevel() visualInfo.visualGroup = visualFieldComp:GetVisualGroup() else for i,v in ipairs(guildVisualList) do if playerID > 0 and v.playerID == playerID then table.remove(guildVisualList, i) isListChanged = true break elseif entityID == v.entityID then table.remove(guildVisualList, i) isListChanged = true break end end end if isListChanged then self.exp:OnCampVisualChanged(entity:GetCampID()) end end end end --↑视野机制--------------------------------------------------------------------------- -- 计算出使用官职buff(这个方法的调用时机:同时进入玩法在BeginLevel ) function SyncLevelFunction:RefreshAllCountryJobBuff() if LevelManager.IsUseCountryJobBuffLevel() then --统计国家 local allCountryList = {} for _,_playerID in ipairs(LevelPlayerManager.PlayerIDList) do local playerInfo = LevelPlayerManager.PlayersArmyTable[_playerID].playerInfo if not table.containValue(allCountryList, playerInfo.countryId) then table.insert(allCountryList, playerInfo.countryId) end end -- 给每个国家添加对应buff for i, countryId in ipairs(allCountryList) do local maxJobLevelData, sourcePlayerID = LevelPlayerManager.GetMaxCountryJobLevelData(countryId) self.countryJobBuffDic[countryId] = {sourcePlayerID = sourcePlayerID, maxCountryJobLevel = maxJobLevelData.Level, buffId = maxJobLevelData.BuffID} local sourceEntity = LevelPlayerManager.GetPlayerActiveEntity(sourcePlayerID) self:_AddBuffToCountryIDEntities(sourceEntity, countryId, maxJobLevelData.BuffID) -- 添加buff -- UI事件派发 EventSystem.Dispatch(EventID.CountryJob_BetterBuff, sourceEntity._logicInitData.Name, maxJobLevelData, countryId) end end end -- entity Active调用 function SyncLevelFunction:RefreshCountryJobBuff_PlayerActive(playerID) if LevelManager.IsUseCountryJobBuffLevel() then local playerInfo = LevelPlayerManager.PlayersArmyTable[playerID].playerInfo local countryId = playerInfo.countryId local countryJobData = DataSheetManager.CountryJob:GetDataByID(playerInfo.countryJobId) if countryJobData == nil then logging.error(LTAG.ERROR, "[国家官职]:CountryJob没有数据 : ", playerInfo.countryJobId) return end if self.countryJobBuffDic[countryId] == nil or countryJobData.Level > self.countryJobBuffDic[countryId].maxCountryJobLevel then -- 需要更换 local sourceEntity = LevelPlayerManager.GetPlayerActiveEntity(playerID) self:_AddBuffToCountryIDEntities(sourceEntity, countryId, countryJobData.BuffID) --更新数据 if self.countryJobBuffDic[countryId] then self.countryJobBuffDic[countryId].sourcePlayerID = playerID self.countryJobBuffDic[countryId].maxCountryJobLevel = countryJobData.Level self.countryJobBuffDic[countryId].buffId = countryJobData.BuffID else self.countryJobBuffDic[countryId] = {sourcePlayerID = playerID, maxCountryJobLevel = countryJobData.Level, buffId = countryJobData.BuffID} end -- UI事件派发 EventSystem.Dispatch(EventID.CountryJob_BetterBuff, playerInfo.name, countryJobData, countryId) else -- 自己添加上已有的最高官阶buff local maxCountryJobBuffInfo = self.countryJobBuffDic[countryId] if maxCountryJobBuffInfo.buffId > 0 then local entitys = LevelPlayerManager.PlayersEntityTable[playerID] local sourceEntity = LevelPlayerManager.GetPlayerActiveEntity(maxCountryJobBuffInfo.sourcePlayerID) for i,myEntity in ipairs(entitys) do SystemManager.AddBuff(myEntity, maxCountryJobBuffInfo.buffId, sourceEntity) end end end end end -- entity退出调用 function SyncLevelFunction:RefreshCountryJobBuff_PlayerQuit(playerID) if LevelManager.IsUseCountryJobBuffLevel() then local playerInfo = LevelPlayerManager.PlayersArmyTable[playerID].playerInfo local countryId = playerInfo.countryId if self.countryJobBuffDic[countryId] and self.countryJobBuffDic[countryId].sourcePlayerID == playerID then -- 当已有提供buff的信息时,且发生变化, 需要换buff --删除旧buff( 可以用buff互斥搞 ,但是有的官职是没有buff的,需要代码进行删除) self:_DeleteBuffToCountryIDEntities(countryId, self.countryJobBuffDic[countryId].buffId) -- 换buff local maxJobLevelData, sourcePlayerID = LevelPlayerManager.GetMaxCountryJobLevelData(countryId) if maxJobLevelData then local sourceEntity = LevelPlayerManager.GetPlayerActiveEntity(sourcePlayerID) self:_AddBuffToCountryIDEntities(sourceEntity, countryId, maxJobLevelData.BuffID) --更新数据 self.countryJobBuffDic[countryId].sourcePlayerID = sourcePlayerID self.countryJobBuffDic[countryId].maxCountryJobLevel = maxJobLevelData.Level self.countryJobBuffDic[countryId].buffId = maxJobLevelData.BuffID else self.countryJobBuffDic[countryId] = nil end end end end function SyncLevelFunction:_AddBuffToCountryIDEntities(entity, countryId, buffID) if buffID > 0 then for _,_playerID in ipairs(LevelPlayerManager.PlayerIDList) do local playerInfo = LevelPlayerManager.PlayersArmyTable[_playerID].playerInfo if playerInfo.countryId == countryId then local entitys = LevelPlayerManager.PlayersEntityTable[_playerID] for i,target in ipairs(entitys) do SystemManager.AddBuff(target, buffID, entity) end end end end end function SyncLevelFunction:_DeleteBuffToCountryIDEntities(countryId, buffID) if buffID > 0 then for _,_playerID in ipairs(LevelPlayerManager.PlayerIDList) do local playerInfo = LevelPlayerManager.PlayersArmyTable[_playerID].playerInfo if playerInfo.countryId == countryId then local entitys = LevelPlayerManager.PlayersEntityTable[_playerID] for i,target in ipairs(entitys) do SystemManager.DeleteBuff(target, buffID) end end end end end function SyncLevelFunction:OnSetCurEntity(entity,oldEntity) -- 不要用Base ,base设置了自动战斗会导致不一致 end function SyncLevelFunction:OnApplicationPause(pause) if not pause then if self.HadStopLevel then EventSystem.Dispatch(EventID.Level_SendRoleLeaveFuben ,true) end end end --region 挂机惩罚相关 -- 统计挂机时长 function SyncLevelFunction:PreHandleCommand(entity, playerID, command) if not self.needRecordPlayerHangUp then return end local curPlayerHangUpState = self.playerHangUpStateDic[playerID] if curPlayerHangUpState == nil then -- 玩家从来没有上线,则此值为nil;玩家上线后,此值为PlayerHangUpState.None self.playerHangUpStateDic[playerID] = PlayerHangUpState.None self.playerHangUpFrameDic[playerID] = 0 end local hasInput = private.HasInput(command) if hasInput then -- 有输入 if curPlayerHangUpState == PlayerHangUpState.Punish then -- 已被惩罚,有输入时,恢复到手动战斗 self:PunishToPunishManualFight(playerID) elseif curPlayerHangUpState == PlayerHangUpState.AutoFight then self:HangUpAutoFightToNone(playerID) elseif curPlayerHangUpState ~= PlayerHangUpState.PunishManualFight then self.playerHangUpStateDic[playerID] = PlayerHangUpState.None end self.playerHangUpFrameDic[playerID] = 0 else -- 无输入 if not entity.components.attr:IsAlive() then return end self.playerHangUpFrameDic[playerID] = self.playerHangUpFrameDic[playerID] + 1 -- 一个Frame有多个cmd,只算一次 if curPlayerHangUpState == PlayerHangUpState.None then if self.playerHangUpFrameDic[playerID] >= Const_PunishForAFKTime1 then -- 无操作达到30秒,开始进入挂机自动战斗 self:HangUpToAutoFight(playerID) end elseif curPlayerHangUpState == PlayerHangUpState.AutoFight then if self.playerHangUpFrameDic[playerID] >= Const_PunishForAFKTime3 then-- 无操作达到30+40秒,由挂机自动战斗提示阶段 到惩罚阶段 self:HangUpAutoFightToPunish(playerID) else if self.playerHangUpFrameDic[playerID] % 40 == 0 then -- 每2秒就提示下玩家赶紧操作 self.exp:HangUpTipsForPunish(playerID, Const_PunishForAFKTime3 - self.playerHangUpFrameDic[playerID]) end end elseif curPlayerHangUpState == PlayerHangUpState.PunishManualFight then -- 已被惩罚,又很久没有输入,则自动战斗 if self.playerHangUpFrameDic[playerID] > Const_PunishForAFKTime1 then-- 无操作达到30秒,开始进入挂机自动战斗 self:PunishManualFightToAuto(playerID) end end end end function private.HasInput(command) local result = command.MoveDir == Const_Vector2_Zero and command.SkillIndex == 0 and (command.SpecialCommand == 0 or command.SpecialCommand > MaxInputSpecialCommand) return not result end --挂机n秒后,进入托管 function SyncLevelFunction:HangUpToAutoFight(playerID) self.playerHangUpStateDic[playerID] = PlayerHangUpState.AutoFight --AI托管 local entitys = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) if entitys then for i,entity in ipairs(entitys) do self:StartAutoFight(entity) end end end -- 弹提示n秒后,标记为挂机惩罚 function SyncLevelFunction:HangUpAutoFightToPunish(playerID) self.playerHangUpStateDic[playerID] = PlayerHangUpState.Punish self.exp:OnHangUpAutoFightToPunish(playerID) end -- 挂机战斗收到输入后,恢复为玩家不挂机手动战斗 function SyncLevelFunction:HangUpAutoFightToNone(playerID) self.playerHangUpStateDic[playerID] = PlayerHangUpState.None --停止AI托管 local entities = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) if entities then for i,_entity in ipairs(entities) do self:StopAutoFight(_entity) end end end -- 已被惩罚,恢复到手动战斗 function SyncLevelFunction:PunishToPunishManualFight(playerID) self.playerHangUpStateDic[playerID] = PlayerHangUpState.PunishManualFight --停止AI托管 local entities = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) if entities then for i,_entity in ipairs(entities) do self:StopAutoFight(_entity) end end end -- 已被惩罚,手动操作恢复到自动战斗 function SyncLevelFunction:PunishManualFightToAuto(playerID) self.playerHangUpStateDic[playerID] = PlayerHangUpState.Punish --停止AI托管 local entities = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) if entities then for i,_entity in ipairs(entities) do self:StartAutoFight(_entity) end end end --endregion 挂机相关 -- Cheat类 ServerAction ↓----------------------------------------------------------------------------------------------- function private.IsCheatServerAction(actionType) return actionType > LuaConst.EServerBattleActionType.CHEAT_START and actionType < LuaConst.EServerBattleActionType.CHEAT_FINISH end function private.ServerActionCreateTestMonster(entity, actionData) local entity = private.ServerActionCreateMonster(entity, actionData) if entity then entity._logicInitData.IsTest = true end end function private.ServerActionSetTestMonsterUseSelfAI(entity, actionData) if actionData.IntParam1 then if actionData.IntParam1 > 0 then NotSyncBattleSetter.Set_IsUseSelfAI(true) else NotSyncBattleSetter.Set_IsUseSelfAI(false) end end end function private.ServerActionControlledAIUseSkill(entity, actionData) NotSyncBattleSetter.SetTestEntitySkillPos(actionData.IntParam1) end function private.ServerActionLevelFinish(entity, actionData, command) LevelItemManager.Global:DoAction(LuaConst.LevelActionType.Global_LevelSucceed) return true end -- Cheat类 ServerAction ↑----------------------------------------------------------------------------------------------- ----- ServerBattleAction ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ --------------------------------------------------- function SyncLevelFunction:CreatePlayerIllusionEntity(entity, pos, dir, aiId, markId) local playerID = entity:GetPlayerID() if playerID <= 0 then logging.error(LTAG.ERROR, "[gws]:创建分身,目前只支持武将 ,如需boss等也需要,则在此加新的逻辑代码 ") return end local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerID) local heroArmyData = LevelPlayerManager.GetPlayerArmyData(playerID,entity) local attr = heroArmyData.Attr EntityFactory.SetParams("CampID", playerInfo.campID, "Name", playerInfo.name) EntityFactory.SetParams("MasterEntity", entity) EntityFactory.SetType_ID(LuaConst.CharacterType.Illusion, heroArmyData.HeroID) EntityFactory.SetParams("StratagemSkill", playerInfo.stratagemSkill) EntityFactory.SetAttrTable_ID(attr) EntityFactory.SetPos_Dir(pos, dir) EntityFactory.SetSelf_Sync(false, false) EntityFactory.SetEffectBreak_Weapon(heroArmyData.mainHeroBreakNums, heroArmyData.isMagicWeaponActive,heroArmyData.mainHeroWeaponLevels) EntityFactory.SetHeroAppearance_ID(heroArmyData.heroAppearanceId) EntityFactory.SetWeaponAppearance_ID(heroArmyData.weaponAppearanceId) EntityFactory.SetDyeAppearanceList(heroArmyData.mainDyeCurPartList) EntityFactory.SetLightEffect_ID(heroArmyData.lightEffectIds) EntityFactory.SetWeaponSoulInfo(heroArmyData.weaponSoulInfo) EntityFactory.SetParams("PlayerID", playerID) local illusionEntity = EntityFactory.DoCreateIllusionEntity() illusionEntity:SetIndex(1) illusionEntity.components.record:VariableSet("illusionMarkId", markId) illusionEntity._logicInitData.elo = playerInfo.elo illusionEntity._logicInitData.TeamID = playerInfo.teamId illusionEntity._logicInitData.GuildID = playerInfo.guildID illusionEntity._logicInitData.TeamLeaderName = playerInfo.teamLeaderName illusionEntity._logicInitData.OfficialPosition = playerInfo.officialPosition illusionEntity:SyncFinish() SystemManager.BindDead(illusionEntity, entity) SystemManager.Active(illusionEntity) illusionEntity.ExpEntity.ignoreUIHPFromMaster = true --属性和buff SystemManager.EntityCopy(illusionEntity, entity) illusionEntity:SetAttrBaseValue("hp",entity:GetHp()) illusionEntity.ExpEntity.ignoreUIHPFromMaster = false illusionEntity.components.aiBase:ChangeAiByAIID(aiId) BattleEventSystem.AfterEntityCreateIllusion(entity, illusionEntity) return illusionEntity end return SyncLevelFunction