-- 最通用的levelFunction local LevelFunction = class("LevelFunction") local EventSystem = require('Event/EventSystem') local fuben_common = require('Proto/fuben_pb') local Infrast = require("Infrast/Infrast") local DEFAULT_DIFFICULTY = 1 --默认难度搞成1 local private = {} local AUTO_FIGHT_STATE_KEY = "AUTO_FIGHT_STATE" local EStatisticType = LuaConst.EStatisticType local SkillMissileSystem = require"Battle/Logic/Entity/System/SkillMissileSystem" local VisualFieldSystem = require "Battle/Logic/Entity/System/VisualFieldSystem" function LevelFunction:GetExpClass() return require "Battle/Expression/ExpLevel/ExpLevelFunction/ExpLevelFunction" end function LevelFunction:initialize() self.PlayerStatisticDic = {} -- key = playerID ,value 是PlayerStatistic(一个table,由CreatePlayerStatistic创建,InitPlayerStatistic初始化,注意InitPlayerStatistic会被各个子类重写) end function LevelFunction:InitLevel() BattleEventSystem.OnInitLevel() self.Difficulty = DEFAULT_DIFFICULTY self.exp = self:GetExpClass():new(self) if LevelPlayerManager.MainEntityCampID == -1 then LevelPlayerManager.MainEntityCampID = LuaConst.STANDALONE_PLAYER_CAMP end self.reviveFromServerCountDic = {} --服务器复活次数, key = playerID, value = 次数 self.bornItemIdList = {} self.bossList = {} --boss的Entity列表(只包含活着的boss),因为boss的顺序比较敏感,要显示双boss血条之类的,所以把所有boss都存在bosslist里 self.timeCostList = {} --通关耗时列表,有的组队关是需要连续通过多个小关卡的,这个列表保存一次组队关的至今为止的所有小关卡的耗时 self:TryResetSomeSystemOrManagerNilMethod() -- 保底代码,防止重定向的方法在一些特殊case下没有设置回正常方法 self:InitLevelDifficulty() self:CalcGodBeastInitValue() self.levelBornBuffs = LevelManager.GetCurInfoData().BornBuffs self.needPauseFrame = false local pauseFrameLevels = DataSheetManager.ConstData:GetDataByID("PauseFrameLevel").IntListValue local curLevelID = LevelManager.GetCurLevelID() for i,levelID in ipairs(pauseFrameLevels) do if levelID == curLevelID then self.needPauseFrame = true break end end self.passTimeOfPrevious = 0, --前面关卡的时间 EventSystem.Dispatch(EventID.Level_InitLevel) self.DamageTimestampDic = {} -- 伤害时间戳统计 key - 受伤的EntityID ,value = Dic{ [攻击者entityID] = 最后伤害的帧数 } (包括最后一刀人) self.LevelStatisticDic = {} -- 关卡统计字典,用来储存关卡全局的统计数据 self.exp:OnInitLevel() self.PlayerID2BornItemWeightPairListDic = {} --玩家出生点权重列表字典,key为PlayerID,value是pair列表,pair的key是出生点元素ID,value是权重 self.CampID2BornItemWeightPairListDic = {} --阵营出生点权重列表字典,key为CampID,value是pair列表,pair的key是出生点元素ID,value是权重 self.BornItemID2BornItemWeightPairListDic = {} --原出生点 - 出生点权重列表字典,key为BornItemID,value是pair列表,pair的key是出生点元素ID,value是权重 self.BornItemID2WeightIdInfo = {} -- 出生点权重的id信息记录,这个会记录所有的出生点权重设置的id信息,元素是id类型和id值。 self.LevelReviveType = LuaConst.LevelReviveType.Normal self.CanSwitchHeroByCmd = true -- 是否可通过cmd(即按钮)切换主将 self.ShowTeammateIcon = true -- 如果复活方式是选择主将复活 ,ShowTeammateIcon 显示队友的头像 self.SwitchHeroInheritSA = true -- 切换主将继承无双怒气值 self.sameGuildCanBeEnemy = true --同势力的玩家是否可以为敌对阵营 self.playerHeroDeadTimeFrame = {} -- 统计玩家的每个玩家的每个entity的死亡时间戳(帧数据) ,没有放入self.PlayerStatisticDic的原因是这个数据有点复杂,不适合放进去 self:SetSelectOneReviveParam() self.positionRecord = {} -- 位置记录列表,用于一些需要把位置转换为index进行通信的地方,比如用command指定寻路到哪个点,此时只能发送index给服务器。这个列表只是一个数据储存区,给一些特定功能使用。 self.saveRecord = {} --关卡存档。会在战斗开始时根据服务器发过来的字符串生成,并且在关卡结束的时候序列化成字符串发给服务器 self.saveRecord = {} --关卡存档。会在战斗开始时根据服务器发过来的字符串生成,并且在关卡结束的时候序列化成字符串发给服务器 self.rewardRecordDic = {} --宝箱奖励记录。 key是playerID,value是record数组。record是一个结构{amount(奖励次数), } self.campVisualDic = {} --阵营共享视野 key为campID,value为一个数组,数组的元素是一个table : {playerID, entityID, visualLevel, visualGroup}. 同阵营的所有单位拥有campID对应的数组里的所有视野 self:InitLevelStatistic() self:InitSaveRecordByBytes(LevelManager.ConsumeServerSaveRecordBytes()) self:InitLevelBySaveRecord() local intermediateParamsBytes = LevelManager.GetServerClientIntermediateParamsBytes() if intermediateParamsBytes then self.intermediateInfoList = self:DeserializeClientIntermediateParamsBytes(intermediateParamsBytes) end end --保底代码,防止重定向的方法在一些特殊case下没有设置回正常方法 function LevelFunction:TryResetSomeSystemOrManagerNilMethod() if LuaShoveManager.EntityShoveIsNil() then LuaShoveManager.SetEntityShoveNotNil() end if CampManager.IsOnEntityCreatedNotSort() then CampManager.SetOnEntityCreatedSort() end if MissileManager.LogicUpdateIsNil() then MissileManager.SetLogicUpdateNotNil() end if SystemManager.LogicUpdateIsNil() then SystemManager.SetLogicUpdateNotNil() end if SkillMissileSystem.LogicUpdateIsNil() then SkillMissileSystem.SetLogicUpdateNotNil() end end function LevelFunction:CalcGodBeastInitValue() if LevelManager.IsSolo() then self.godBeastInitValue = 30 elseif LevelManager.IsPVPLevel() then self.godBeastInitValue = DataSheetManager.ConstData:GetDataByID("GodBeastPowerPVP").IntValue else self.godBeastInitValue = DataSheetManager.ConstData:GetDataByID("GodBeastPowerPVE").IntValue end end function LevelFunction:GetGodBeastInitValue() return self.godBeastInitValue end function LevelFunction:SetSelectOneReviveParam() self.RebornBaseTime = DataSheetManager.ConstData:GetDataByID("HeroRebornBaseTime").IntValue -- 基础复活时间 s self.RebornExtraTime = DataSheetManager.ConstData:GetDataByID("HeroRebornExtraTime").IntValue -- 死亡次数额外附加时间 s self.RebornMaxTime = DataSheetManager.ConstData:GetDataByID("HeroRebornMaxTime").IntValue -- 最大复活时间 end --关卡元素初始化完毕的时间点 function LevelFunction:AfterLevelLayoutInited() end function LevelFunction:IsWaiting() return false --子类重写该方法以标示关卡是否处于等待阶段 end --是否三军武将同时上场 function LevelFunction:IsAllHeroJoinBattle() return self.isAllHeroJoinBattle or false end function LevelFunction:SetIsAllHeroJoinBattle(val) self.isAllHeroJoinBattle = val end --是否三军武将独立计算无双值 function LevelFunction:IsIndividualSa() return false end function LevelFunction:TryCreatePlayerStatistic(playerID) if not self.PlayerStatisticDic[playerID] then self:CreatePlayerStatistic(playerID) end end function LevelFunction:CreatePlayerStatistic(playerID) local statistic = {} self.PlayerStatisticDic[playerID] = statistic self:InitPlayerStatistic(statistic) if self.LevelReviveType == LuaConst.LevelReviveType.SelectOne then local armyTable = LevelPlayerManager.GetPlayerArmyByPlayerIDInRoom(playerID) local firstHero = armyTable[1] and armyTable[1].HeroID or -1 local secondHero = armyTable[2] and armyTable[2].HeroID or -1 local thirdHero = armyTable[3] and armyTable[3].HeroID or -1 self.playerHeroDeadTimeFrame[playerID] = {[LuaConst.ArmyPosType.First] = {heroID = firstHero}, [LuaConst.ArmyPosType.Second] = {heroID = secondHero} ,[LuaConst.ArmyPosType.Third] = {heroID = thirdHero} } -- 0,2,4 代表前中后军 end end function LevelFunction:InitLevelStatistic() self.LevelStatisticDic[EStatisticType.MonsterDeadCountDic] = {} end function LevelFunction:InitPlayerStatistic(statistic) statistic[EStatisticType.DeadCount] = 0 statistic[EStatisticType.SwitchHeroCount] = 0 end function LevelFunction:MonsterDeadCountStatisticAdd(monsterID) local monsterDeadCountDic = self.LevelStatisticDic[EStatisticType.MonsterDeadCountDic] monsterDeadCountDic[monsterID] = monsterDeadCountDic[monsterID] or 0 monsterDeadCountDic[monsterID] = monsterDeadCountDic[monsterID] + 1 end function LevelFunction:GetMonsterDeadCount(monsterID) return self.LevelStatisticDic[EStatisticType.MonsterDeadCountDic][monsterID] or 0 end function LevelFunction:AddRewardRecord(playerID, boxRewardID, addAmount) addAmount = addAmount or 1 self.rewardRecordDic[playerID] = self.rewardRecordDic[playerID] or {} local index = nil for i,rewardRecord in ipairs(self.rewardRecordDic[playerID]) do if rewardRecord.boxRewardID == boxRewardID then rewardRecord.amount = rewardRecord.amount + addAmount index = i break end end if not index then local record = {boxRewardID = boxRewardID, amount = addAmount} table.insert(self.rewardRecordDic[playerID], record) end self.exp:OnAddRewardRecord(playerID, boxRewardID, addAmount) --logging.error(LTAG.ERROR, "HYW : AddRewardRecord : playerID : ", playerID, "boxRewardID : ", boxRewardID, "addAmount : ", addAmount, " now : ", self.rewardRecordDic[playerID][boxRewardID]) end function LevelFunction:TryAddLvBuFairAddingBuff(entity) if LevelManager.IsFairLevel() then if entity:GetCharacterID() == LuaConst.LVBU_HERO_ID then local lvbuAddingBuffConst = DataSheetManager.ConstData:GetDataByID("CnFairLvBuAddingBuff") --目前国服、日服会有CnFairLvBuAddingBuff的ConstData配置,hmt没有。这里用了判断是否有配置来决定是否改变吕布公平竞技被动生效次数 if lvbuAddingBuffConst and lvbuAddingBuffConst.IntValue then local success, buffData = DataSheetManager.BuffData:TryGetDataByBuffID(lvbuAddingBuffConst.IntValue) if success then SystemManager.AddBuff(entity, lvbuAddingBuffConst.IntValue, entity) end end end end end --尝试添加服务器发来的玩家buff,这类buff是针对玩家所有的entity的 function LevelFunction:TryAddPlayerBuff(playerID) local playerBuffs = LevelPlayerManager.GetPlayerBuffInfoList(playerID) if playerBuffs and #playerBuffs > 0 then local entities = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) if entities then for i,entity in ipairs(entities) do for j,buffInfo in ipairs(playerBuffs) do self:AddBuffByServerBuffInfo(entity, buffInfo) end end end end end --这里的buffInfo是proto里的BuffInfo结构 function LevelFunction:AddBuffByServerBuffInfo(entity, buffInfo) local buffCtrl = RuntimeDataManager.New_BuffCtrl() buffCtrl.ForceDuration:Reset(buffInfo.duration) SystemManager.AddBuff(entity, buffInfo.buffId, entity, nil, buffCtrl) end function LevelFunction:InitLevelDifficulty() if LevelManager.IsNormal() then local data = LevelManager.GetBarrierChosenIDByLevelID(LevelManager.GetCurLevelID()) if data then self.Difficulty = data.DifficultyDegree self.LevelGs = data.Battle self.SubjugateDataPVEID = data.SubjugateDataPVE end elseif LevelManager.IsOfficalPositionLevel() then for _,data in DataSheetManager.OfficialPositionPVE:GetEnumerator() do --策划保证,OfficialPositionPVE表里不会有行重复同一个LevelID,所以这里可以这样搞 if data.LevelID == LevelManager.GetCurLevelID() then self.LevelGs = data.Recommend self.SubjugateDataPVEID = data.SubjugateDataPVE break end end elseif LevelManager.IsPersonalBoss() then for _,data in DataSheetManager.PersonalBoss:GetEnumerator() do --策划保证,BarrierChosenData表里不会有行重复同一个MissonDataID,所以这里可以这样搞 if data.MissonDataID == LevelManager.GetCurLevelID() then self.LevelGs = data.Battle self.SubjugateDataPVEID = data.SubjugateDataPVE self.Difficulty = data.Level --logging.error(LTAG.ERROR, "HYW : 设置个人Boss关卡战力 : ", self.LevelGs) break end end else self.LevelGs = 0 self.Difficulty = DEFAULT_DIFFICULTY end end function LevelFunction:GetStandardPlayerAttrDic(entity, recommandGs) if self.standardPlayerAttrDic == nil then local FormulaCalculator = require"Battle/Logic/Missile/FormulaCalculator" if recommandGs == nil then recommandGs = LevelManager.GetServerGs() if recommandGs == 0 or recommandGs == nil then --2022年6月27日 EValueAdaptiveType.BY_FORMULA_SERVER_GS 类型的自适应优先使用服务器战力,其次使用关卡战力 recommandGs = self:GetLevelGs() end end self.standardPlayerAttrDic = FormulaCalculator.CalculateStandardPlayerAttribute(recommandGs) end return self.standardPlayerAttrDic end function LevelFunction:GetMonsterValueDataByPowerPoint(entity) return DataSheetManager.MonsterValueDataByPowerPoint:GetDataByMonsterValueID(entity._logicInitData._characterData.AdaptiveParam) end -----------------权重出生点机制----------------------------------------- function LevelFunction:GetBornItemByIndex(index) return LevelItemManager.GetBornItem(index) end local EIDType = { CAMP = 1, BORNITEM = 2, PLAYER = 3, } --可能返回nil function LevelFunction:GetBornItemListOfCampID(campID) for i,idInfo in ipairs(self.BornItemID2WeightIdInfo) do local idType = idInfo[1] local id = idInfo[2] if idType == EIDType.CAMP then if id == campID then local dic = self:GetBornItemID2WeightListDicAndRefreshFunc(idType, id) local idWeightPairsList = dic[id] local bornItemList = {} for j,pair in ipairs(idWeightPairsList) do bornItemList[#bornItemList + 1] = pair.key end return bornItemList end end end end function LevelFunction:GetWeightSumOfPairList(pairList) local weightSum = 0 for i,pair in ipairs(pairList) do weightSum = weightSum + pair.value end return weightSum end --要求pairList里异地 function LevelFunction:GetRandomElementInPairList(pairList) local weightSum = pairList.weightSum or self:GetWeightSumOfPairList(pairList) local rand = LogicRandom.GetIntegerValue(nil, 1, weightSum ,55454319) local sum = 0 for i,intPair in ipairs(pairList) do sum = sum + intPair.value if sum >= rand then return intPair end end end function LevelFunction:GetBornItemID2WeightListDicAndRefreshFunc(idType, id) local dic = nil local refreshFunc = nil if idType == EIDType.CAMP then refreshFunc = self.TryRecalculateBornItemIDByCampID dic = self.CampID2BornItemWeightPairListDic elseif idType == EIDType.BORNITEM then refreshFunc = self.TryRecalculateBornItemIDByBornItemID dic = self.BornItemID2BornItemWeightPairListDic elseif idType == EIDType.PLAYER then refreshFunc = self.TryRecalculateBornItemIDOfPlayerID dic = self.PlayerID2BornItemWeightPairListDic end return dic, refreshFunc end -- idWeightPairsList的元素是一个table,包含key和value两个字段,分别存bornItemID和权重 function LevelFunction:AddBornItemID2WeightList(idInfo, idWeightPairsList) local idType = idInfo[1] local id = idInfo[2] local dic, refreshFunc = self:GetBornItemID2WeightListDicAndRefreshFunc(idType, id) if dic then local weightSum = 0 for i,pair in ipairs(idWeightPairsList) do weightSum = weightSum + pair.value end idWeightPairsList.weightSum = weightSum dic[id] = idWeightPairsList table.insert(self.BornItemID2WeightIdInfo, idInfo) if refreshFunc then refreshFunc(self, id, idWeightPairsList) EventSystem.Dispatch(EventID.Level_SetBornItem) --else --正常情况,某些时候不需要刷新 end else logging.error(LTAG.ERROR, "HYW : GetBornItemID2WeightListDicAndRefreshFunc 错误!!! dic : ", dic, " refreshFunc : ", refreshFunc) end end function LevelFunction:TryRecalculateBornItemIDByCampID(campID, idWeightPairsList) idWeightPairsList = idWeightPairsList or self.CampID2BornItemWeightPairListDic[campID] self:SetBornItemIDOfEntityListByWeightList(CampManager.GetEntityListOfCampId(campID), idWeightPairsList) end function LevelFunction:TryRecalculateBornItemIDByBornItemID(bornItemID, idWeightPairsList) idWeightPairsList = idWeightPairsList or self.BornItemID2BornItemWeightPairListDic[bornItemID] self:SetBornItemIDOfEntityListByWeightList(LevelPlayerManager.GetHeroEntitiesOfBornItemID(bornItemID), idWeightPairsList) end function LevelFunction:TryRecalculateBornItemIDOfPlayerID(playerID, idWeightPairsList) idWeightPairsList = idWeightPairsList or self.PlayerID2BornItemWeightPairListDic[playerID] self:SetBornItemIDOfEntityListByWeightList(LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID), idWeightPairsList) end function LevelFunction:SetBornItemIDOfEntityListByWeightList(entityList, idWeightPairsList) if idWeightPairsList then local idWeightPair = self:GetRandomElementInPairList(idWeightPairsList) if idWeightPair then if entityList and #entityList > 0 then --logging.error(LTAG.ERROR, "HYW : Set bornItemID : ", idWeightPair.key, " to Entity ", entityList[1].EntityID) for i,entity in ipairs(entityList) do if entity.isHero then --只有英雄才设置 bornItemID entity._logicInitData:SetBornItemID(idWeightPair.key) end end end else logging.error(LTAG.ERROR, "HYW : 出生点权重配置错误!!!") end --else --正常情况,默认没有出生点权重列表 end end function LevelFunction:SetBornItemIDOfPlayerByWeightListWithIndex(playerID, index) local idWeightPairsList = self.PlayerID2BornItemWeightPairListDic[playerID] if idWeightPairsList == nil then local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(playerID) if playerInfo == nil then logging.error(LTAG.ERROR, "HYW : playerInfo nil Error @LevelFunction:SetBornItemIDOfPlayerByWeightListWithIndex! playerID : ", playerID) return end idWeightPairsList = self.CampID2BornItemWeightPairListDic[playerInfo.campID] if idWeightPairsList == nil then local entity = LevelPlayerManager.GetPlayerActiveEntity(playerID) if entity and entity._logicInitData.BornItemID then idWeightPairsList = self.BornItemID2BornItemWeightPairListDic[entity._logicInitData.OriginBornItemID] end end end if idWeightPairsList then local entityList = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) if entityList and #entityList > 0 and index then local idWeightPair = idWeightPairsList[index] if idWeightPair then for i,entity in ipairs(entityList) do entity._logicInitData:SetBornItemID(idWeightPair.key) end else logging.error(LTAG.ERROR, "HYW : 选择了不存在的出生点, index : ", index, " 关卡ID : ", LevelManager.GetCurLevelID()) end end else if playerID > 0 then logging.error(LTAG.ERROR, "HYW : 必须调用关卡行为 【全局_设置出生点|复活点|复活时生效】 给玩家设置了一组出生点后,才可以给玩家按照选择的index设置出生点") else logging.error(LTAG.ERROR, "HYW : 警告!尝试给非玩家单位设置可选出生点,这是不支持的!") end end end --根据bornItemID2WeightList重新随机1个出生点 function LevelFunction:TryRecalculateBornItemIDOfPlayer(campID, bornItemID, playerID) for i,idInfo in ipairs(self.BornItemID2WeightIdInfo) do local isMeet = false local idType = idInfo[1] local id = idInfo[2] if idType == EIDType.CAMP then isMeet = id == campID elseif idType == EIDType.BORNITEM then isMeet = id == bornItemID elseif idType == EIDType.PLAYER then isMeet = id == playerID end if isMeet then local dic = self:GetBornItemID2WeightListDicAndRefreshFunc(idType, id) local idWeightPairsList = dic[id] self:TryRecalculateBornItemIDOfPlayerID(playerID, idWeightPairsList) break end end end -----↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑权重出生点机制↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑---------------------------------------------------- --↓视野机制--------------------------------------------------------------------------- function LevelFunction:CanSeeEntity(seeingEntity, beSawEntity) local result = true if (not self.sameGuildCanBeEnemy and LevelPlayerManager.IsSameGuild(seeingEntity, beSawEntity)) or (not CampManager.IsEnemyEntity(seeingEntity, beSawEntity)) then --如果是同阵营 ,或者是同工会且同工会的人不能为敌的玩法(比如世界boss),则可以看到 result = true else result = VisualFieldSystem.CanSeeEntityByVisualLevel(seeingEntity, beSawEntity) end if not result then result = VisualFieldSystem.CanSeeEntityByShareVisualLevel(seeingEntity, beSawEntity, self.campVisualDic) end return result end function LevelFunction:OnVisualFieldChanged(entity, oldLevel, oldGroup) local visualFieldComp = entity.components.visualField --这里不能用originEntity! local playerID = entity:GetPlayerID() local entityID = entity.EntityID local campID = entity:GetCampID() local campVisualList = self.campVisualDic[campID] if not campVisualList then campVisualList = {} self.campVisualDic[campID] = campVisualList end local isListChanged = false if visualFieldComp:GetVisualLevel() > 0 then local visualInfo for i,v in ipairs(campVisualList) 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 --logging.error(LTAG.ERROR, "HYW : insert 高阶视野: ", entityID) table.insert(campVisualList, visualInfo) isListChanged = true end visualInfo.visualLevel = visualFieldComp:GetVisualLevel() visualInfo.visualGroup = visualFieldComp:GetVisualGroup() else for i,v in ipairs(campVisualList) do if playerID > 0 and v.playerID == playerID then table.remove(campVisualList, i) isListChanged = true --logging.error(LTAG.ERROR, "HYW : remove a entity : ", v.entityID) break elseif entityID == v.entityID then table.remove(campVisualList, i) isListChanged = true --logging.error(LTAG.ERROR, "HYW : remove b entity : ", v.entityID) break end end end if isListChanged then self.exp:OnCampVisualChanged(campID) end end --↑视野机制--------------------------------------------------------------------------- --armyData是LevelPlayerManager.GetPlayerArmyByPlayerIDInRoom返回的list里的元素 function LevelFunction:GetGsByArmyData( gs, armyData ) --默认规则直接返回第一个参数指定的gs值 return gs end function LevelFunction:SetServerGsByLowestPlayerGs() local lowestGs local curGs for k,armyData in pairs(LevelPlayerManager.PlayersArmyTable) do --pairs safe 数据查询对顺序不敏感 curGs = 0 for i,heroArmyData in ipairs(armyData.army) do curGs = curGs + heroArmyData.heroGs end if curGs > 0 and (lowestGs == nil or curGs < lowestGs) then lowestGs = curGs end end LevelManager.SetServerGs(lowestGs) end --AI延迟启动的帧数,联机玩法会设置大于0的值,延迟AI的启动,用于减轻关卡一开始的性能压力 function LevelFunction:GetHeroAiDelayFrame() return 0 end function LevelFunction:CurLevelCanSwitchHeroByCmd(fromEntity, index) return self.CanSwitchHeroByCmd end --关卡是否可以手动切换战斗模式 function LevelFunction:CanSwitchFightModeManually() return true end --英雄出生时是否需要添加 "可使用技能" Buff function LevelFunction:ShouldAddCanUseSkillToHeroWhenBorn() return true end function LevelFunction:GetBornPosAndDirByArmyIndex(i, originBornPos, bornDir) local offset = FP.V2.RotateAroundOrigin(bornDir, FP.Const.PI_HALF) local strideConst = DataSheetManager.ConstData:GetDataByID("HeroBornStride") if strideConst then offset:Mul(strideConst.FloatValue) end if i == 1 then return originBornPos, bornDir elseif i == 2 then return originBornPos + offset, bornDir elseif i == 3 then return originBornPos - offset, bornDir end end function LevelFunction:CreatePlayers(pos,dir) local playerInfo = LevelPlayerManager.GetPlayerInfoByPlayerIDInRoom(LuaConst.STANDALONE_PLAYER_PlayID) local army = LevelPlayerManager.GetPlayerArmyByPlayerIDInRoom(LuaConst.STANDALONE_PLAYER_PlayID) if not army then return end local bornItem = LevelItemManager.GetBornItem(LuaConst.PlayerDefaultBornIndex) if (self.usePlayerInfoPos or LevelManager.IsUsePlayerInfoPos()) and playerInfo.Pos then pos = playerInfo.Pos end if pos then self.bornPos = pos else self.bornPos = LogicRandom.GetRandomPointInLevelItem(nil, bornItem.ItemData, 8238238) -- new 支持 固定/范围随机 位置出生 end self.bornDir = dir or bornItem.ItemData.ItemDir if LevelManager.IsSyncCityWarSideWaitingArea() or LevelManager.IsSyncCityWarMainWaitingArea() then if FP.tonumberV1(pos.z) > 150 then self.bornDir:Reset(0, -1) else self.bornDir:Reset(0, 1) end end local heroEntityTable = {} local gs = 0 for i,v in ipairs(army) do gs = gs + (v.heroGs or 0) end --日常副本、挖宝位面本 要求设置自身战力为服务器战力(2022年11月14日) --需求又改为仅日常需设置自身战力为服务器战力了 (2023年1月13日) -- 主播pc包也需要设置自身战力为服务器战力(2023-3-22) if LevelManager.IsDaily() or LevelManager.IsGameStreamerLevel() then LevelManager.SetServerGs(gs) end -- local offset = FP.V2.RotateAroundOrigin(self.bornDir, FP.Const.PI_HALF) -- local strideConst = DataSheetManager.ConstData:GetDataByID("HeroBornStride") -- if strideConst then -- offset:Mul(strideConst.FloatValue) -- end for i,v in ipairs(army) do EntityFactory.SetParams("BornItemID", bornItem.LevelItemID, "InitHP", v.HP, "Name", playerInfo.name) EntityFactory.SetType_ID(LuaConst.CharacterType.Hero, v.HeroID) EntityFactory.SetParams("CampID", LuaConst.STANDALONE_PLAYER_CAMP) EntityFactory.SetParams("PlayerID", LuaConst.STANDALONE_PLAYER_PlayID) EntityFactory.SetParams("StratagemSkill", playerInfo.stratagemSkill) EntityFactory.SetAttrTable_ID(v.Attr) -- if i == 2 then -- EntityFactory.SetPos_Dir(self.bornPos + offset, self.bornDir) -- elseif i == 3 then -- EntityFactory.SetPos_Dir(self.bornPos - offset, self.bornDir) -- else -- EntityFactory.SetPos_Dir(self.bornPos, self.bornDir) -- end EntityFactory.SetPos_Dir(self:GetBornPosAndDirByArmyIndex(i, self.bornPos, self.bornDir, v.HeroID)) EntityFactory.SetSelf_Sync(true, true) EntityFactory.SetEffectBreak_Weapon(v.mainHeroBreakNums,v.isMagicWeaponActive,v.mainHeroWeaponLevels) EntityFactory.SetHorse_ID(v.horseID, v.horseAppearanceId) if v.petInfo then EntityFactory.SetPet_Info(v.petInfo.petId, v.petInfo.stage, v.petInfo.petName, v.petInfo.petSkill) EntityFactory.SetPet_TitleInfo(v.petInfo.isLineUp, v.petInfo.runeSkillLevelSum, v.petInfo.aptitudeSum) end EntityFactory.SetHeroAppearance_ID(v.heroAppearanceId) EntityFactory.SetWeaponAppearance_ID(v.weaponAppearanceId) EntityFactory.SetDyeAppearanceList(v.mainDyeCurPartList) EntityFactory.SetLightEffect_ID(v.lightEffectIds) EntityFactory.SetWeaponSoulInfo(v.weaponSoulInfo) EntityFactory.SetTitle_ID(playerInfo.titleID) local entity = EntityFactory.DoCreatePlayerEntity() self:TryAddLvBuFairAddingBuff(entity) --entity._logicInitData.PlayerIDInRoom = LuaConst.STANDALONE_PLAYER_PlayID entity._logicInitData.OfficialPosition = playerInfo.officialPosition entity:SetGs(self:GetGsByArmyData(gs, v)) table.insert(heroEntityTable,entity) local adjutantID = v.AdjutantID if adjutantID and adjutantID > 0 then EntityFactory.SetType_ID(LuaConst.CharacterType.Adjutant, adjutantID) EntityFactory.SetAttrTable_ID(v.Attr) EntityFactory.SetEffectBreak_Weapon(v.viceHeroBreakNums,v.isMagicWeaponActive,v.viceHeroWeaponLevels) EntityFactory.SetHeroAppearance_ID(v.viceHeroAppearanceId) EntityFactory.SetWeaponAppearance_ID(v.viceWeaponAppearanceId) EntityFactory.SetDyeAppearanceList(v.viceDyeCurPartList) EntityFactory.DoCreateAdjutantEntity(entity) end end LevelPlayerManager.SetPlayerEntityTable(heroEntityTable ,LuaConst.STANDALONE_PLAYER_PlayID) --设置激活主将 local curEntity = BattleDataGetter.GetHeroInHeroEntityTableByIndex(LuaConst.STANDALONE_PLAYER_PlayID,1) LevelPlayerManager.SetCurEntity(curEntity ,false , LevelPlayerManager.GetPlayerSetting(curEntity._logicInitData.PlayerIDInRoom ,LuaConst.BattleSettingEnum.AutoRide)) if self:IsAllHeroJoinBattle() then for index = 2, 3 do local entity = BattleDataGetter.GetHeroInHeroEntityTableByIndex(LuaConst.STANDALONE_PLAYER_PlayID, index) if entity then SystemManager.Active(entity, false) self:StartAutoFight(entity) end end end self:InitEntityByClientIntermediateParamsBytes() self:OnCreatePlayer() end function LevelFunction:OnCreatePlayer() self:TryCreatePlayerStatistic(LuaConst.STANDALONE_PLAYER_PlayID) self:TryAddPlayerBuff(LuaConst.STANDALONE_PLAYER_PlayID) if LevelManager.IsNormal() then if self:GetInitAutoFightState() then local entity = BattleDataGetter.GetHeroInHeroEntityTableByIndex(LuaConst.STANDALONE_PLAYER_PlayID, 1) if entity then SystemManager.StartAutoFight(entity) end end end EventSystem.Dispatch(EventID.Level_OnCreatePlayerAllEntitys,LuaConst.STANDALONE_PLAYER_PlayID) end function LevelFunction:InitEntityByClientIntermediateParamsBytes() if self.intermediateInfoList then for i,playerInfo in ipairs(self.intermediateInfoList) do local bornItem = nil if playerInfo.bornItemId > 0 then bornItem = LevelItemManager.GetItemByID(playerInfo.bornItemId) if not bornItem then logging.error(LTAG.ERROR, "HYW : 跨地图传送,不存在关卡配置的关卡元素,关卡元素ID : " .. tostring(playerInfo.bornItemId), " 关卡ID:", LevelManager.GetCurLevelID()) end end local entities = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerInfo.playerID) for index,entity in ipairs(entities) do if entity:GetCharacterID() == playerInfo.curCharacterID then --设置初始武将 if index ~= 1 then LevelPlayerManager.SwitchHero(playerInfo.playerID,index,false) end end local deadCount = 0 --设置属性 for i,entityInfo in ipairs(playerInfo.entityInfos) do if entity:GetCharacterID() == entityInfo.characterID then if deadCount < 2 or entityInfo.hp ~= 0 then entity:SetAttrBaseValue(LuaConst.RealtimeAttrType.hp,entityInfo.hp) EventSystem.Dispatch(entity.Event_EntityHPChanged, entity, entityInfo.hp - entity:GetAttrValueNum(LuaConst.AttrType.hplmt), entityInfo.hp) entity:SetAttrBaseValue(LuaConst.RealtimeAttrType.sa,entityInfo.sa) if entityInfo.hp == 0 then deadCount = deadCount + 1 end end end end if bornItem then SystemManager.OnConvey(entity, bornItem.ItemData.ItemPos, bornItem.ItemData.ItemDir) end end end end end --以LevelBattleArmy数据表的配置创建Players --armyData是LevelBattleArmy的一行数据,代表了一套最多6个英雄的阵容(3组主副将) function LevelFunction:CreatePlayers_ByArmyData(armyData,bornItemID,pos,dir, isAllHeroBattle) local army = LevelPlayerManager.GetPlayerArmyByPlayerIDInRoom(LuaConst.STANDALONE_PLAYER_PlayID) local attrTable = army and army[1].Attr or nil --下标为1即为当前的武将相关阵容信息 --TODO:gws local heroEntityTable = {} for i,v in ipairs(armyData.ArmyData_Table) do local position = pos if isAllHeroBattle then -- 全部出战时,需要一字摆开 间隔4秒 if i == 2 then position = FP.V2.GetOffsetToForward(dir,FP.newV2(4,0)):Add(pos) elseif i == 3 then position = FP.V2.GetOffsetToForward(dir,FP.newV2(-4,0)):Add(pos) end end if v.HeroValueID == 0 and attrTable then EntityFactory.SetAttrTable_ID(attrTable) else --EntityFactory.SetAttrTable_ID(nil, v.HeroValueID) EntityFactory.SetOneParam("AttrID", v.HeroValueID) end EntityFactory.SetParams("BornItemID", bornItemID) EntityFactory.SetType_ID(LuaConst.CharacterType.Hero, v.HeroDataID) EntityFactory.SetPos_Dir(position, dir) EntityFactory.SetSelf_Sync(true, true) EntityFactory.SetEffectBreak_Weapon(v.MainHeroBreakNums,v.isActiveMagicWeapon, 0) EntityFactory.SetHorse_ID(v.HorseID, v.horseAppearanceId) EntityFactory.SetHeroAppearance_ID(v.HeroAppearanceId) EntityFactory.SetWeaponAppearance_ID(v.HeroMagicWeaponID) EntityFactory.SetParams("PlayerID", LuaConst.STANDALONE_PLAYER_PlayID) local entity = EntityFactory.DoCreatePlayerEntity() entity:SetGs(self:GetGsByArmyData(v.MainGs, v)) --entity._logicInitData.PlayerIDInRoom = LuaConst.STANDALONE_PLAYER_PlayID -- hardcode ↓ 定死规则,LevelBattleArmy表里,ArmyData_Table各个元素的pos约定死了 local posTemp = nil if i == 1 then posTemp = 2 --base.proto里 HeroLineUpPosition.Mid elseif i == 2 then posTemp = 0 --base.proto里 HeroLineUpPosition.Left else posTemp = 4 --base.proto里 HeroLineUpPosition.Right end --↑ if v.ViceHeroDataID > 0 then if v.ViceHeroValueID == 0 and attrTable then EntityFactory.SetAttrTable_ID(attrTable) else EntityFactory.SetAttrTable_ID(nil, v.ViceHeroValueID) end EntityFactory.SetType_ID(LuaConst.CharacterType.Adjutant, v.ViceHeroDataID) EntityFactory.SetEffectBreak_Weapon(v.ViceHeroBreakNums,v.isActiveMagicWeapon, 0) EntityFactory.SetHeroAppearance_ID(v.ViceHeroAppearanceId) EntityFactory.SetWeaponAppearance_ID(v.ViceHeroMagicWeaponID) EntityFactory.DoCreateAdjutantEntity(entity) LevelPlayerManager.SetPlayerArmyPosInfo(v.ViceHeroDataID, posTemp) end table.insert(heroEntityTable,entity) LevelPlayerManager.SetPlayerArmyPosInfo(v.HeroDataID, posTemp) entity:SetSideHeroForceAiID(v.AIID) end LevelPlayerManager.SetPlayerEntityTable(heroEntityTable, LuaConst.STANDALONE_PLAYER_PlayID, true) --设置激活主将 local curEntity = BattleDataGetter.GetHeroInHeroEntityTableByIndex(LuaConst.STANDALONE_PLAYER_PlayID,1) --继承无双值 if LogicUpdateManager._curEntity then curEntity:SetAttrBaseValue(LuaConst.RealtimeAttrType.sa, LogicUpdateManager._curEntity:GetAttrValue(LuaConst.RealtimeAttrType.sa)) end LevelPlayerManager.SetCurEntity(curEntity) EventSystem.Dispatch(EventID.UIBattle_Level_CreateEntityByData) if isAllHeroBattle then for index = 2, 3 do local entity = BattleDataGetter.GetHeroInHeroEntityTableByIndex(LuaConst.STANDALONE_PLAYER_PlayID, index) if entity then SystemManager.Active(entity, false) self:StartAutoFight(entity) end end end end function LevelFunction:BeginLevel() LogicUpdateManager.StartFightUpdate() LevelItemManager.Start() LevelEvaluationManager.StartEvaluations() self:ResetLevelRemainTime() if LevelManager.IsTown then NotSyncBattleSetter.ChangeSceneFindPath() end self.exp:OnBeginLevel() end function LevelFunction:ResetLevelRemainTime() if self.passTimeOfPrevious and self.passTimeOfPrevious ~= 0 then local leftTime = FP.newV1(0) leftTime:Reset(LevelManager.GetCurInfoData().Timer):Sub(self.passTimeOfPrevious) LevelItemManager.Global:DoAction(LuaConst.LevelActionType.Global_SetRemainTime, nil, leftTime) end end function LevelFunction:StopLevel() self.HadStopLevel = true LogicUpdateManager.StopFightUpdate() if self.exp then -- 这个为nil的情况是存在的。还未InitLevel就收到了结算信息,则由于房间消失 进入战斗过程中断了。 self.exp:OnStopLevel() end end function LevelFunction:GetHadStopLevel() return self.HadStopLevel end function LevelFunction:OnLoadFinished() -- CompareSyncLogManager.ClearLog() end function LevelFunction:PreUpdate(frame) end function LevelFunction:OnUpdate() self:TryActiveServerPlayerBuff() end function LevelFunction:TryActiveServerPlayerBuff() if LevelManager.IsStandaloneBattleLevel() then --仅单机战斗才允许ServerPlayerBuff机制。 联机玩法用插帧来添加buff if LevelPlayerManager.HasWaitingPlayerBuffInfo() then LuaInputManager.SetSpecialCommand(LuaConst.SpecialCommandType.ActiveServerPlayerBuff) end end end function LevelFunction:OnLateUpdate() if self.exp then self.exp:Update() end end function LevelFunction:GetBattleInfo() return nil end function LevelFunction:GetLevelGs() return self.LevelGs or 0 end --子类可以重写这个方法,比如金将体验关需要动态生成这个数据 function LevelFunction:GetSubjugateDataPVE() local subjugateDataPVEID = LevelManager.GetSubjugateDataPVEID() if subjugateDataPVEID>0 then return DataSheetManager.SubjugateDataPVE:GetDataByID(subjugateDataPVEID) end end function LevelFunction:GetSubjugateDataPVEID() return self.SubjugateDataPVEID or 0 end function LevelFunction:GetLevelDifficulty() return self.Difficulty end --可能返回nil function LevelFunction:GetMonsterShowRangeSqr() return nil end --返回Boss优先级,越小代表优先级越高,1代表第一顺位Boss。 -1代表没有任何Boss function LevelFunction:GetBossPriority(entityID) for i,v in ipairs(self.bossList) do if v.EntityID == entityID then return i end end return -1 end function LevelFunction:GetMainBoss() return self.bossList[1] end function LevelFunction:GetBossList() return self.bossList end --获取公式自适应变量,基础规则是以主玩家的等级为变量来自适应。仅可用于单机副本,联机副本需要重写 function LevelFunction:GetFormulaAdaptiveVariable() if LevelManager.IsServerNeedCheckNotSyncLevel() or LevelManager.IsSyncLevel() then logging.error(LTAG.ERROR, "[GWS][HYW] : 严重错误 ! 校验关卡 不支持公式自适应变量 !!! 关卡ID : ", LevelManager.GetCurLevelID()) end return PlayerInfoManager.GetMainPlayerLv() end function LevelFunction:GetFormulaAdaptVarRangeConstPrefix() return "MonsterAdaptedLevelRange_" end --获取公式自适应的分段,目前只有这一种规则,就是根据常量表里配置的数分段。如果其他关卡需要修改常量表变量名,重写GetFormulaAdaptVarRangeConstPrefix即可 function LevelFunction:GetFormulaAdaptiveVariableRange(variable) if not self.variableRange then local variableRange = 1 local prefix = self:GetFormulaAdaptVarRangeConstPrefix() for i=1,999 do --最多支持999段,正常情况下会提前跳出,所以这里写999这么大的数也没事,策划应该也不会配置超过999分段的公式 local ID = prefix .. i local rangeInfo = DataSheetManager.ConstData:GetDataByID(ID) if rangeInfo then if variable >= rangeInfo.IntListValue[1] and variable <= rangeInfo.IntListValue[2] then variableRange = i break end else logging.error(LTAG.ERROR, "HYW : 没有找到正确的自适应分段 关卡类型 : ", LevelManager.GetCurLevelType() ," ,等级:" ,variable) break end end self.variableRange = variableRange end return self.variableRange end function LevelFunction:OnSetCurEntity(entity,oldEntity) if entity and oldEntity then if oldEntity.components.autoFight.IsStandaloneAuto or oldEntity.components.autoFight.IsSyncAuto then self:StartAutoFight(entity) end end end --这个方法里的index的含义,需要LevelFucntion子类自己定义,并跟UI那边约定好。 比如世界boss的index代表伤害列表里队伍的index(以前世界boss里是代表player的index) function LevelFunction:SwtichForceTargetByIndex(entity, index) --do nothing logging.error(LTAG.ERROR, "HYW : 未实现的方法 SwtichForceTargetByIndex") end function LevelFunction:GetLevelAutoFightInitRule() if LevelManager.IsNeedCheckNotSyncLevel() then --单机校验必须关闭自动战斗 if not LevelManager.IsNormal() then --2023年4月21日新增 主线剧情需要校验的关卡,不关闭自动战斗,而是读取服务器设置 return LuaConst.ELevelAutoFightInitRule.CLOSE end end if LevelManager.IsPlanesLevel() then return LuaConst.ELevelAutoFightInitRule.LOAD_DEFAULT_OPEN elseif LevelManager.CheckCurLevelType(LuaConst.LevelType.Biography) then return LuaConst.ELevelAutoFightInitRule.OPEN else return LuaConst.ELevelAutoFightInitRule.LOAD_DEFAULT_CLOSE end end function LevelFunction:GetAutoFightStateSaveKey() return (PlayerInfoManager.AcID or "") .. AUTO_FIGHT_STATE_KEY .. tostring(LevelManager.GetCurLevelType()) end --获取关卡初始化自动战斗状态 --仅单机玩法生效 --联机玩法由各自的LevelFunction管理 function LevelFunction:GetInitAutoFightState() local state = false local rule = self:GetLevelAutoFightInitRule() if rule == LuaConst.ELevelAutoFightInitRule.OPEN then state = true elseif rule == LuaConst.ELevelAutoFightInitRule.CLOSE then state = false else if LevelManager.IsNormal() then --2023年4月21日新增需求,主线剧情(包括精英和地狱难度)读取服务器设置 local savedState = LevelPlayerManager.GetPlayerSetting(LuaConst.STANDALONE_PLAYER_PlayID, LuaConst.BattleSettingEnum.AutoFightNormalLevel) if rule == LuaConst.ELevelAutoFightInitRule.LOAD_DEFAULT_OPEN then if savedState == nil then state = true else state = savedState end elseif rule == LuaConst.ELevelAutoFightInitRule.LOAD_DEFAULT_CLOSE then if savedState == nil then state = false else state = savedState end else logging.error(LTAG.ERROR, "HYW : 关卡没有指定自动战斗初始化规则 关卡类型 : ", LevelManager.GetCurLevelType()) end else --走本地设置 --本地数据迁移开始 local key = self:GetAutoFightStateSaveKey() local saveTb = PersistentData.DeviceData:Get("AUTO_FIGHT_STATE") if UIUtils.HasPlayerPrefsKey(key) then local oldValue = UIUtils.GetPlayerPrefsIntValue(key) UIUtils.DeletePlayerPrefsValue(key) saveTb[LevelManager.GetCurLevelType()] = oldValue PersistentData.DeviceData:Set("AUTO_FIGHT_STATE", saveTb, true) end --本地数据迁移结束 local savedState = (saveTb and saveTb[LevelManager.GetCurLevelType()]) and saveTb[LevelManager.GetCurLevelType()] or 0 if rule == LuaConst.ELevelAutoFightInitRule.LOAD_DEFAULT_OPEN then if savedState == 0 then state = true else state = (savedState == 1 and true or false) end elseif rule == LuaConst.ELevelAutoFightInitRule.LOAD_DEFAULT_CLOSE then if savedState == 0 then state = false else state = (savedState == 1 and true or false) end else logging.error(LTAG.ERROR, "HYW : 关卡没有指定自动战斗初始化规则 关卡类型 : ", LevelManager.GetCurLevelType()) end --↑ end end return state end function LevelFunction:SwitchAutoFight(entity) local robotAI = self:GetRobotAI(entity) if robotAI then entity.components.aiBase:SetSingleCurrentAiByID(robotAI) end entity.components.autoFight:SwitchPveAutoFightState() entity.components.target:SetForceTarget(nil) end function LevelFunction: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:StartPveAutoFight(isSystemLevel) end function LevelFunction:StopAutoFight(entity) entity.components.autoFight:StopPveAutoFight() entity.components.target:SetForceTarget(nil) end local MAX_POSITION_RECORD_COUNT = 32768 --最大坐标记录个数。因为self.positionRecord的index会被command的一个short表示,所以不能超过short的最大值。预期应该不会超过32768,除非玩家分享坐标3W多次。 --添加坐标记录,并返回这个坐标记录的index。如果已经添加过,则不会再次添加,会返回之前添加时的index function LevelFunction:AddPositionRecord(pos) local existIndex for i,_pos in ipairs(self.positionRecord) do if _pos == pos then existIndex = i break end end if existIndex then return existIndex else table.insert(self.positionRecord, pos) if #self.positionRecord >= MAX_POSITION_RECORD_COUNT then logging.error(LTAG.ERROR, "HYW : 错误!!!添加位置数量过多,超过的32768,会导致RunToPosByIndex失效") end return #self.positionRecord end end function LevelFunction:RunToPosByIndex(entity, index) if index < MAX_POSITION_RECORD_COUNT then local pos = self.positionRecord[index] if pos then entity.components.aiBase:SetRunToDynamicPos(pos) if not entity.components.autoFight:IsAuto() then self:StartAutoFight(entity, false, LuaConst.RunToPosByIndexAIID) end else logging.error(LTAG.ERROR, "HYW : RunToPosByIndex失败,没有index : ", index, " 对应的pos") end else logging.error(LTAG.ERROR, "HYW : 错误!!!添加位置数量过多,超过的32768,RunToPosByIndex失败") end end function LevelFunction:CloseMusouShow() self.isMusouShowOpen = false end function LevelFunction:CloseMusouShowOfHero(heroId) self.heroId2MusouShowFlagDic = self.heroId2MusouShowFlagDic or {} self.heroId2MusouShowFlagDic[heroId] = false local entity = LevelPlayerManager.GetPlayerEntityByCharacterID(LevelPlayerManager.MyPlayerIDInRoom, heroId) if entity then entity.components.skillBase:ReplaceMusouSkillToNoShowVersion(entity, entity:GetOriginCharacterData()) entity.components.skillBase:TryReplaceSkillByMagicWeapon(entity:GetOriginCharacterData()) --else --正常情况,比如此时entity还没有被创建出来。这里不用做特别处理,因为entity创建时也会ReplaceMusouSkillToNoShowVersion end end function LevelFunction:OpenMusouShow() self.isMusouShowOpen = true end function LevelFunction:IsMusouShowOpen(entity) local isOpen = true if entity:IsHero() then if LevelManager.IsNeedCheckNotSyncLevel() then return false end if self.isMusouShowOpen == false then isOpen = false else if self.heroId2MusouShowFlagDic and self.heroId2MusouShowFlagDic[entity:GetCharacterID()] == false then isOpen = false else isOpen = true end end --else --非Hero单位,统一返回true --do nothing end if entity.isRetinue then isOpen = false end return isOpen end function LevelFunction:GetRobotAI(entity) if entity:IsMainEntityOfPlayer() then return LuaConst.PveRobotAIID else --返回副单位AI(副单位:三将同时上场时,没有被玩家操控的那些英雄单位) return entity:GetSideHeroForceAiID() or LuaConst.SideHeroDefaultAI end end function LevelFunction:IsAutoUseMusou(entity) return LevelPlayerManager.GetPlayerSetting(entity:GetPlayerID(), LuaConst.BattleSettingEnum.AutoMusou) end function LevelFunction:IsEntityResAmountReachLimit(entity) return false end function LevelFunction:IsCollectAmountReachLimit(entity, key) logging.error(LTAG.ERROR, "HYW : 该关卡未实现 IsCollectAmountReachLimit 方法 LevelID : ", LevelManager.GetCurLevelID()) return false end function LevelFunction:LeftBattleTimeFrame() return 0 end --Entity:initialized() 之后 function LevelFunction:OnEntityBorn(entity) if entity.isBoss then self:OnBossBorn(entity) elseif entity.isHero then self:OnHeroBorn(entity) elseif entity.isRetinue then self:OnRetinueBorn(entity) end end function LevelFunction:OnRetinueBorn(entity) LevelPlayerManager.TryApplyRetinueDevelopEffects(entity) end function LevelFunction:OnHeroBorn(entity) if self.levelBornBuffs then for i,buffId in ipairs(self.levelBornBuffs) do SystemManager.AddBuff(entity, buffId, entity) end end --添加顿帧buff PauseFrame if self.needPauseFrame then -- 关卡是否需要顿帧 local find, pauseFrameData = DataSheetManager.PauseFrameData:TryGetDataByHeroID(entity:GetCurCharacterID()) if find then for i,buffId in ipairs(pauseFrameData.Buffs) do SystemManager.AddBuff(entity, buffId, entity) end end end self.exp:OnHeroBorn(entity) end local BossPrioritySorter = function(a, b) return a.components.base.CurMonsterFeature.BossPriority < b.components.base.CurMonsterFeature.BossPriority end function LevelFunction:OnBossBorn(entity) table.insert(self.bossList, entity) table.sort(self.bossList, BossPrioritySorter) EventSystem.Dispatch(EventID.UIBattle_Level_OnBossBorn, entity) self:OnBossListChanged()--这个最后调用 self.exp:OnBossBorn(entity) end function LevelFunction:OnBackground(entity) SystemManager.SetVisual(entity, 0, nil) SystemManager.SetVisibility(entity, 0, nil) end function LevelFunction:OnEntityAddProp(entity, btnIndex, battlePropData) end function LevelFunction:OnEntityDead(entity, deadParams, missileID) if entity.isMonster then --统计怪物死亡数作为基础功能 self:MonsterDeadCountStatisticAdd(entity:GetCharacterID(), entity) end if entity.isBoss then self:OnBossDead(entity) elseif entity.isHero then self:OnHeroDead(entity, missileID) elseif entity.isIllusion then self:OnIllusionDead(entity) elseif entity.isVehicle then self:OnVehicleDead(entity) end SystemManager.SetVisual(entity, 0, nil) end function LevelFunction:OnBossDead(entity) for i,v in ipairs(self.bossList) do if v == entity then table.remove(self.bossList, i) self:OnBossListChanged(entity, true) break end end end function LevelFunction:OnHeroDead(entity, missileID) self:HandleHeroDead(entity, entity.components.record.KillerEntity, missileID) end function LevelFunction:HandleHeroDead(entity, killerEntity) local playerID = entity:GetPlayerID() if playerID then local dStatistic = self.PlayerStatisticDic[playerID] if dStatistic then dStatistic[EStatisticType.DeadCount] = dStatistic[EStatisticType.DeadCount] + 1 if entity.components.record.lastHpLoss >= entity:GetMaxHp() then --是否是被秒杀 dStatistic[EStatisticType.OnePunchKilledNum] = (dStatistic[EStatisticType.OnePunchKilledNum] or 0) + 1 end end end end --载具死亡时 function LevelFunction:OnVehicleDead(entity) end function LevelFunction:OnBossListChanged(entity, isDead) EventSystem.Dispatch(EventID.UIBattle_Level_OnBossListChanged, entity, isDead) end function LevelFunction:OnPlayerOneHeroDead(entity) if self.LevelReviveType == LuaConst.LevelReviveType.Normal then local alldead = true local autoIndex = -1 alldead,autoIndex = LevelPlayerManager.IsAllDead(entity) if self.GetHeroDeadSwitchIndex then autoIndex = self:GetHeroDeadSwitchIndex() end if alldead then self:OnPlayerAllHeroDead(entity) else --自动切人 LevelPlayerManager.SwitchHero(entity._logicInitData.PlayerIDInRoom,autoIndex , true) end elseif self.LevelReviveType == LuaConst.LevelReviveType.SelectOne then local playerID = entity:GetPlayerID() if playerID > 0 then local characterID = entity._logicInitData.CharacterID local pos = LevelPlayerManager.GetHeroArmyPos(playerID,characterID) -- 判断是前中后军 0,2,4 local deadInfo = self.playerHeroDeadTimeFrame[playerID][pos] deadInfo.DeadFrame = LogicUpdateManager._clientHandleFrame -- 本军死亡时间 deadInfo.DeadTimes = (deadInfo.DeadTimes or 0) + 1 --本军死亡次数 local waitSeconds = math.min(self.RebornBaseTime + (deadInfo.DeadTimes-1) * self.RebornExtraTime , self.RebornMaxTime) deadInfo.ReviveTotalTime = waitSeconds deadInfo.ReviveFrame = LogicUpdateManager._clientHandleFrame + waitSeconds * LuaConst.LogicFrameRate-- 最快复活时间 self.exp:OnPlayerOneHeroDead_SelectOne(self.playerHeroDeadTimeFrame,playerID ,entity) if entity._logicInitData.BattleMemberType == LuaConst.BattleMemberType.Robot_Hero then self:OnRobotHeroDead_SelectOne(entity) end end end end function LevelFunction:OnNotifyAI(entity, id) end function LevelFunction:OnRobotHeroDead_SelectOne(entity) local entitys = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(entity._logicInitData.PlayerIDInRoom) if entitys then for index,toEntity in ipairs(entitys) do if toEntity.components.attr:IsAlive() then LevelPlayerManager.SwitchHero(entity._logicInitData.PlayerIDInRoom,index , true) break end end end end function LevelFunction:OnPlayerAllHeroDead(entity) self.exp:OnPlayerAllHeroDead(entity) end function LevelFunction:GetSelectOneTypeReviveInfo(playerID) local deadInfo = self.playerHeroDeadTimeFrame[playerID] if deadInfo then local firstInfo = deadInfo[LuaConst.ArmyPosType.First] local secondInfo = deadInfo[LuaConst.ArmyPosType.Second] local thirdInfo = deadInfo[LuaConst.ArmyPosType.Third] return firstInfo, secondInfo, thirdInfo end return nil end function LevelFunction:GetSelectOneRevivePunishTime() return nil end -- 某个entity复活时间到了 function LevelFunction:ReviveTimeIsUp(entity) end --返回要播放胜利pose的entity function LevelFunction:GetVictoryPoseEntity() if LevelManager.IsSyncLevel() then logging.error(LTAG.ERROR, "HYW : 致命错误! 同步关卡中使用了主视角代码!请立即联系战斗程序修正!") end return LogicUpdateManager._curEntity end --这个东西会影响战斗状态,所以不能放在Exp里,只能放在逻辑里 function LevelFunction:ShowVictoryPose() --播放胜利动画 local entity = self:GetVictoryPoseEntity() ExpSystemManager.ResetMaterial(entity) --有时候Entity会被修改材质(比如冰冻), 这里强行Reset一次恢复成默认材质 SystemManager.DeleteAllTimeScale(entity) ExpSystemManager.StopAllPsCamera() ExpSystemManager.SetCameraGrayState(false) local victoryID = DataSheetManager.ConstData:GetDataByID("ScenesVictoryShowPoint").IntValue local itemData = LevelItemManager.GetItemByID(victoryID).ItemData local heroData = DataSheetManager.HeroNum:GetDataByID(entity._logicInitData.CharacterID) local victoryPoseEffect = entity.components.replace:GetVictoryPose(heroData.VictoryPose) local winEffect = "Public/Win/"..victoryPoseEffect SystemManager.OnWinBattle(entity, itemData.ItemPos, itemData.ItemDir,winEffect) end --只要是有血量改变意图,即便最终血量没有任何改变,也会调用该方法 function LevelFunction:OnHpChange(entity, caster, delta, op, realDelta) end function LevelFunction:SetPlayerActive(playerID) logging.error(LTAG.ERROR, "HYW : 未实现的 SetPlayerActive!!!! 如果是随进随出玩法,必须实现SetPlayerActive") end --entity active 后 function LevelFunction:OnEntityActive(entity) end function LevelFunction:QuitRoom(entity) logging.error(LTAG.ERROR, "HYW : 未实现的 QuitRoom!!!! 如果是随进随出玩法,必须实现QuitRoom") end --↓↓↓↓↓↓↓↓↓↓↓中间关参数处理↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ local PLAYER_START_STR = "@@player" local TIME_COST_PREFIX = "time:" local TIME_COST_PREFIX_LEN = #TIME_COST_PREFIX local SPLITOR = "," function LevelFunction:AppendStr(origin,appendant) origin = origin .. appendant .. SPLITOR return origin end --返回下一关 playerID对应玩家的出生点的元素ID,-1代表默认出生点 function LevelFunction:GetNextLevelBornItemId(playerID) return self.bornItemIdList[playerID] or -1 end function LevelFunction:GetPassTimeOfPrevious() return self.passTimeOfPrevious end -- new↓ 使用binser来做序列化和反序列化 function LevelFunction:FillIntermediateInfoOfPlayer(playerID, info) local entities = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) local playerInfo = {} info[#info + 1] = playerInfo playerInfo.playerID = playerID playerInfo.bornItemId = self:GetNextLevelBornItemId(playerID) playerInfo.curCharacterID = entities[1]:GetCharacterID() local entityInfos = {} playerInfo.entityInfos = entityInfos for i,entity in ipairs(entities) do entityInfos[i] = {} entityInfos[i].characterID = entity:GetCharacterID() entityInfos[i].hp = entity:GetAttrValueNum(LuaConst.RealtimeAttrType.hp) entityInfos[i].sa = entity:GetAttrValueNum(LuaConst.RealtimeAttrType.sa) end return info end function LevelFunction:FillTimeCostInfo(info) info.timeCostList = {} if self.timeCostList then for i,timeCost in ipairs(self.timeCostList) do info.timeCostList[i] = timeCost end info.timeCostList[#info.timeCostList + 1] = BattleDataGetter.GetSummaryTime() else info.timeCostList[1] = BattleDataGetter.GetSummaryTime() end return info end -- new↑ function LevelFunction:GetClientIntermediateParamsBytes() -- new↓ 使用binser来做序列化和反序列化 if LevelManager.HasNextLevel() then local info = {} --把血量、无双值、传送点按照顺序拼成字符串传给服务器。服务器会在下一关转发回来 self:FillIntermediateInfoOfPlayer(LuaConst.STANDALONE_PLAYER_PlayID, info) --附上每一小关的通关耗时 self:FillTimeCostInfo(info) return Infrast.EncodeToBytes(info) else return "" end -- new↑ end function LevelFunction:DeserializeClientIntermediateParamsBytes(bytes) -- new↓ 使用binser来做序列化和反序列化 local intermediateInfoList = Infrast.DecodeFromBytes(bytes) if type(intermediateInfoList) == "table" then self.timeCostList = intermediateInfoList.timeCostList self.passTimeOfPrevious = 0 if self.timeCostList then for i,passTime in ipairs(self.timeCostList) do self.passTimeOfPrevious = self.passTimeOfPrevious + passTime end end return intermediateInfoList else return nil end -- new↑ end function private.IsPlayerInfoSeg(stringSeg) return stringSeg ~= "" and string.find(stringSeg, TIME_COST_PREFIX) == nil end --↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ --↓↓↓↓↓↓↓↓关卡存档(SaveRecord)相关↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ function LevelFunction:InitSaveRecordByBytes(bytes) if bytes then local temp = Infrast.DecodeFromBytes(bytes) if type(temp) == "table" then self.saveRecord = temp end end end function LevelFunction:InitLevelBySaveRecord() if type(self.saveRecord) == "table" then self:InitLevelItemBySaveRecord(self.saveRecord.levelItems) end end function LevelFunction:InitLevelItemBySaveRecord(levelItemsRecord) if levelItemsRecord then for levelItemID,dic in pairs(levelItemsRecord) do -- pairs safe for k,v in pairs(dic) do -- pairs safe if v and v > 0 then if k == "dontActiveOnStart" then LevelItemManager.SetDontActiveOnStartItemId(levelItemID) elseif k == "dontBornMonster" then LevelItemManager.SetDontBornMonsterItemIdDic(levelItemID) end end end end end end function LevelFunction:SetSaveRecordValue(key, value) self.saveRecord[key] = value end function LevelFunction:GetSaveRecordValue(key) return self.saveRecord[key] end function LevelFunction:SetSaveRecordValue_LevelItem(levelItemID, key, value) if levelItemID then self.saveRecord.levelItems = self.saveRecord.levelItems or {} self.saveRecord.levelItems[levelItemID] = self.saveRecord.levelItems[levelItemID] or {} self.saveRecord.levelItems[levelItemID][key] = value else logging.error(LTAG.ERROR, "HYW : levelItemID nil Error @LevelFunction:SetSaveRecordValue_LevelItem") end end --获取关卡存档字符串 function LevelFunction:GetSaveRecordBytes() local str = nil if self.saveRecord then str = Infrast.EncodeToBytes(self.saveRecord) end return str end --↑↑↑↑↑↑↑↑↑↑关卡存档相关↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ function LevelFunction:OnDrive(driver, beDrivedEntity) if driver then local statistic = self.PlayerStatisticDic[driver:GetPlayerID()] if statistic then statistic[EStatisticType.FlagIsDriving] = true end end end function LevelFunction:OnOffDrive(driver, beDrivedEntity) if driver then local statistic = self.PlayerStatisticDic[driver:GetPlayerID()] if statistic then statistic[EStatisticType.FlagIsDriving] = false end end end function LevelFunction:OnRevive(entity,isInBornPoint) --复活所有人 local heroEntitys = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(entity._logicInitData.PlayerIDInRoom) for i,heroEntity in ipairs(heroEntitys) do SystemManager.OnRevive(heroEntity,isInBornPoint) end --切换到第一个人 --看需求(下面两行代码是同事打开和不打开的) --LevelPlayerManager.SwitchHero(1) --return true,-1,LuaConst.StateType.None self.exp:OnRevive(entity) return true end -- reviveInBornPoint 是否是出生点复活 ,当其为nil时 用self.isSwitchOneReviveNeerBy function LevelFunction:TryReviveAndSwitchOneHero(entity,index, reviveInBornPoint, isFromServer, systemForceRevive) local playerID = entity._logicInitData.PlayerIDInRoom if isFromServer then self.reviveFromServerCountDic[playerID] = self.reviveFromServerCountDic[playerID] or 0 self.reviveFromServerCountDic[playerID] = self.reviveFromServerCountDic[playerID] + 1 end local armyTable = LevelPlayerManager.GetPlayerArmyByPlayerIDInRoom(playerID) if armyTable == nil then logging.error(LTAG.ERROR, "【gws】:TryReviveAndSwitchOneHero ,but armyTable == nil , playerID = , ",playerID) return end 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 or systemForceRevive then -- deadInfo.ReviveFrame为nil表示hero从来没死过 if not entity:IsAlive() then local isInBornPoint = self.isSwitchOneReviveNeerBy if reviveInBornPoint ~= nil then isInBornPoint = reviveInBornPoint end SystemManager.OnRevive(entity, isInBornPoint, nil, self.shouldReviveAtNearestBornPoint) end local errorCode = LevelPlayerManager.SwitchHero(entity._logicInitData.PlayerIDInRoom,index,true) if errorCode == 0 or errorCode == 2 then --errorCode == 2 表示复活当前死亡的英雄,也需要抛出事件 if errorCode == 2 then BattleEventSystem.AfterSelectOneReviveCurHero(entity) end BattleEventSystem.AfterSelectOneRevive(entity) end EventSystem.Dispatch(EventID.UI_SelectOneReviveOver, entity) return true else logging.error(LTAG.ERROR,"[GWS] :",entity:GetPlayerID(),"号玩家选择一个hero复活,但玩家界面还没CD好呢,难道是UI没有拦截住?此log不阻塞代码执行 。index = ",index, ",对应复活armyPos =" ,armyPos ,"复活帧 =" , deadInfo.ReviveFrame, ",当前帧:",LogicUpdateManager._clientHandleFrame) return false end end function LevelFunction:GetReviveFromServerCount(playerID) return self.reviveFromServerCountDic[playerID] or 0 end function LevelFunction:HandleSwitchHeroData(heroEntitys, fromEntity, switchToIndex, toEntity, playerID, isOriginalArmy) heroEntitys[switchToIndex] = fromEntity fromEntity:SetIndex(switchToIndex) heroEntitys[1] = toEntity toEntity:SetIndex(1) if isOriginalArmy then --如果是初始阵容,那么PlayerArmyTable顺序也改变,这里HeroArmyTable起到的作用是记录顺序,方便切换阵容后恢复回来保证顺序跟切换前一致 local army = LevelPlayerManager.GetPlayerArmyByPlayerIDInRoom(playerID) local fromHeroArmy = army[1] local toHeroArmy = army[switchToIndex] army[switchToIndex] = fromHeroArmy army[1] = toHeroArmy end end function LevelFunction:OnSwitchHero(fromEntity,toEntity) self.exp:OnSwitchHero(fromEntity, toEntity) toEntity.components.record:OnSwitchHeroFrom(fromEntity) local statistic = self.PlayerStatisticDic[fromEntity:GetPlayerID()] if statistic then statistic[EStatisticType.SwitchHeroCount] = statistic[EStatisticType.SwitchHeroCount] + 1 end end function LevelFunction:AfterSwitchHero(fromEntity, toEntity, dontBackground) end --关卡传送时 function LevelFunction:OnLevelConvey( ... ) self.bornItemIdList = {} if ... then for i,bornItemId in ipairs(...) do table.insert(self.bornItemIdList,bornItemId) end end end function LevelFunction:OnOffLine(playerID) end function LevelFunction:OnReconnect(playerID) end function LevelFunction:OnPlayerFirstOnline(playerID) end function LevelFunction:ChangeRival(source, target, rivalType) end function LevelFunction:ShouldShowVictoryPose() return not LevelManager.IsPlanesLevel() end --结束关卡之前,此时怪物没有被清空(LevelItem_Global:HandleEndLevel会强制清空怪物,导致LevelFunction:OnLevelSucceed、LevelFunction:OnLevelFail时怪物已经没了,丢失了所有怪物的信息,所以如果需要获取怪物信息,只能在BeforeEndLevel时获取 function LevelFunction:BeforeEndLevel() end function LevelFunction:OnLevelSucceed(withShow) for i,playerID in ipairs(LevelPlayerManager.PlayerIDList) do local entity = LevelPlayerManager.GetPlayerActiveEntity(playerID) if entity then entity.components.record:RefreshLongestHpLossIntervalFrame() end end if LevelManager.HasNextLevel() then withShow = false else withShow = not LevelManager.IsPlanesLevel() and withShow if withShow then if self:ShouldShowVictoryPose() then self:ShowVictoryPose() end end end self:GetBattleSummaryInfoForCheck(true) self.exp:OnLevelSucceed(withShow) self:StopLevel() if LogicUpdateManager.IsReplayOrServerCheck then -- 客户端重播或服务器校验 BattleSimulator.EndPlay() end end function LevelFunction:OnLevelFail() self:GetBattleSummaryInfoForCheck(false) self.exp:OnLevelFail() if LogicUpdateManager.IsReplayOrServerCheck then -- 客户端重播或服务器校验 BattleSimulator.EndPlay() end self:StopLevel() end function LevelFunction:GetPlayerHasReward(playerID) return true end function LevelFunction:GetPlayer_HeroKDA() return nil end function LevelFunction:GetPlayer_HeroRelaxKDA() return nil end function LevelFunction:GetLevelScore() return 0 end function LevelFunction:PreHandleCommand(entity, playerID, command) end function LevelFunction:GetPlayerBattleInfo(playerID) logging.error(LTAG.ERROR , "程序注意:是不是使用GetGuildWarBattleInfo_MySelf,没有判断是哪个场景呢!(此log不会阻挡运行)") return nil end function LevelFunction:GetPlayerDeadHeroCount(playerID) if self.PlayerStatisticDic then local statistic = self.PlayerStatisticDic[playerID] if statistic then return statistic[EStatisticType.DeadCount] else logging.warn(LTAG.WARN, "【gws】当获取玩家死亡主将次数时,没有现在这个玩家信息,则认为是0。playerID =" ,playerID ,",当前关卡ID =" ,LevelManager.GetCurLevelID()) return 0 end end return 0 end function LevelFunction:GetAllPlayerDeadHeroCount() local count = 0 for playerID,statistic in pairs(self.PlayerStatisticDic) do --pairs safe 数据统计对顺序不敏感 count = count + statistic[EStatisticType.DeadCount] end return count end --服务器单机校验需要的代码 (在成功和失败的时候调用) 这个和 LevelSelect_Model中的 RoleFinishFubenRequest 保持一致 function LevelFunction:GetBattleSummaryInfoForCheck(isWin) if USE_LUA_STANDALONE then if LevelManager.IsNeedCheckNotSyncLevel() then local msg = fuben_common.FinishFubenReq() msg.isWin = isWin --三星条件 local evaluationList = LevelEvaluationManager.EvaluationList if evaluationList then for i,v in ipairs(evaluationList) do table.insert(msg.star, v.Completed and 1 or 0) end end local mapType = BattleDataGetter.GetLevelInfoData().LevelType msg.mapType = mapType msg.passTime = math.ceil(BattleDataGetter.GetSummaryTime() + BattleDataGetter.GetPassTimeOfPrevious()) msg.passMilTime = math.ceil((BattleDataGetter.GetSummaryTime() + BattleDataGetter.GetPassTimeOfPrevious()) * 1000) msg.BossHp = BattleDataGetter.GetBossHpLeftPercent() or 1 msg.heroDeadNum = BattleDataGetter.GetPlayerDeadHeroCount(BattleDataGetter.GetCurPlayerID()) msg.saveRecordStr = BattleDataGetter.GetCurLevelSaveRecordBytes() if self.dmgToBoss then msg.bossDamage = FP.tonumberV1(self.dmgToBoss) end if self.bossMaxHp then msg.bossMaxHp = self.bossMaxHp end if self.GetArmyDamageList then local armyDmgList = self:GetArmyDamageList() for i,v in ipairs(armyDmgList) do table.insert(msg.armyDamage, v) end end local rewardRecords = self.rewardRecordDic[LevelPlayerManager.MyPlayerIDInRoom] if rewardRecords then for i,record in ipairs(rewardRecords) do for j=1,record.amount do table.insert(msg.boxRewardIds, record.boxRewardID) end end end local maxHpSum = FP.newV1(0) local hpSum = FP.newV1(0) local entityList = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(LevelPlayerManager.MyPlayerIDInRoom) for i,v in ipairs(entityList) do local data = fuben_common.HeroId2Hp() local attrComp = v.components.attr data.heroId = tostring(v._logicInitData.CharacterID) maxHpSum:Add(attrComp:MaxHP()) hpSum:Add(attrComp.hp) data.hp = tostring(FP.tonumberV1(attrComp.hp) / FP.tonumberV1(attrComp:MaxHP())) data.totalHp = FP.tonumberV1(attrComp:MaxHP()) table.insert(msg.heroId2Hp, data) end if maxHpSum > FP.Const.Zero then table.insert(msg.leftHpRation, FP.tonumberV1(hpSum / maxHpSum)) else table.insert(msg.leftHpRation, 0) end maxHpSum:Reset(0) hpSum:Reset(0) entityList = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(LuaConst.STANDALONE_ENEMYPLAYER1_PLAYID) if entityList then for i,v in ipairs(entityList) do maxHpSum:Add(v.components.attr:MaxHP()) hpSum:Add(v.components.attr.hp) end if maxHpSum > FP.Const.Zero then table.insert(msg.leftHpRation, FP.tonumberV1(hpSum / maxHpSum)) else table.insert(msg.leftHpRation, 0) end end msg.CheckInfo = self:GetBattleResultCheckStr() if self.dmgToBoss then msg.CheckInfo = msg.CheckInfo .. ";dmg2Boss =" .. FP.tonumberV1(self.dmgToBoss) end if rewardRecords then msg.CheckInfo = msg.CheckInfo .. ";boxReward:" for i,id in ipairs(rewardRecords) do msg.CheckInfo = msg.CheckInfo .. id .."," end end msg.score = self:GetLevelScore() if LevelManager.IsRogueLike() then for _, v in ipairs(BattleDataGetter.GetRogueLikeSelectSkills()) do table.insert(msg.chooseSkill, v) end end msg.Req.TIDs = '' msg.Req.PassthroughID ="" BattleSimulator.SetCheckResult(msg.CheckInfo , msg:SerializeToString()) logging.log(LTAG.LOG, " BattleCheck - finishFubenMsg " ) end end end function LevelFunction:GetBattleResultCheckStr() local result = "-" -- 服务器认为""有特殊含义 local entityList = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(LevelPlayerManager.MyPlayerIDInRoom) for i,entity in ipairs(entityList) do result = result .. self:GetBattleResultCheckStrOfEntity(entity) end return result end function LevelFunction:GetBattleResultCheckStrOfEntity(entity) local moveComp = entity.components.move return 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]) end function LevelFunction:IsRivalPlayer(sourcePlayer, checkPlayerID) return LuaConst.RivalType.None end function LevelFunction:OnStratagemEnterCD(entity) if self:IsAllHeroJoinBattle() then local playerID = entity:GetPlayerID() local comp_Stratagem = entity.components.stratagem local entitys = LevelPlayerManager.GetPlayerEntitysByPlayerIDInRoom(playerID) if entitys then for i, otherEntity in ipairs(entitys) do if entity ~= otherEntity then otherEntity.components.stratagem.StratagemCurCD = comp_Stratagem.StratagemTotalCD -- 注意:千万别用SystemManager.StratagemEnterCD(entity)否则死循环 end end else logging.error(LTAG.ERROR, "[gws]: 在设置所有武将计略进入CD时,发现找不到玩家的Entity,playerID =", playerID) end end end --打扫战场(清理子弹,清空怪物,玩家全部为idle,技能call清空,副将隐藏) function LevelFunction:ClearBattleInfo(clearMonster) if clearMonster then local normalCount = #LogicUpdateManager.EntityList_Normal for i = normalCount, 1, -1 do local entity = LogicUpdateManager.EntityList_Normal[i] if entity._logicInitData.CharacterType == LuaConst.CharacterType.Monster or entity._logicInitData.CharacterType == LuaConst.CharacterType.Illusion then SystemManager.DeadQuit(entity) end end end --玩家 for i,v in ipairs(LogicUpdateManager.EntityList_Sync) do local baseComp = v.components.base if baseComp.IsActive or baseComp.HidingNeedUpdate then SystemManager.BreakState(v) -- 主将停止 --副将隐藏 local adjutant = v.components.adjutant.AdjutantEntity if adjutant and adjutant:IsActive() then SystemManager.BreakState(adjutant) -- 停止 SystemManager.Background(adjutant) end end end SkillMissileSystem.Clear() MissileManager.Clear() end function LevelFunction: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 if LevelPlayerManager.IsOriginalArmy() then heroArmyData = LevelPlayerManager.GetPlayerArmyData(playerID,entity) else -- 如果是关卡内替换武将的情况 heroArmyData = private.GetArmyDataByLevelItemData(entity) if heroArmyData.HeroValueID > 0 then EntityFactory.SetOneParam("AttrID", heroArmyData.HeroValueID) end end local attr = heroArmyData.Attr EntityFactory.SetParams("CampID", entity:GetCampID(), "Name", playerInfo.name) EntityFactory.SetParams("MasterEntity", entity) EntityFactory.SetType_ID(LuaConst.CharacterType.Illusion, heroArmyData.HeroID) EntityFactory.SetParams("StratagemSkill", playerInfo.stratagemSkill) EntityFactory.SetAttrTable_ID(attr, heroArmyData.HeroValueID) 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.GuildID = playerInfo.guildID illusionEntity._logicInitData.OfficialPosition = playerInfo.officialPosition 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 function private.GetArmyDataByLevelItemData(entity) local heroID = entity:GetCharacterID() local heroArmyData = {HeroID = heroID} local armyConfigList = LevelPlayerManager.GetSwitchArmyData().ArmyData_Table local army = LevelPlayerManager.GetPlayerArmyByPlayerIDInRoom(entity:GetPlayerID()) local attrTable = army and army[1].Attr or nil for i,v in ipairs(armyConfigList) do if v.HeroDataID == heroID then if v.HeroValueID == 0 and attrTable then heroArmyData.Attr = attrTable else heroArmyData.HeroValueID = v.HeroValueID end heroArmyData.mainHeroBreakNums = v.MainHeroBreakNums heroArmyData.isMagicWeaponActive = v.isActiveMagicWeapon heroArmyData.mainHeroWeaponLevels = 0 heroArmyData.weaponAppearanceId = v.HeroMagicWeaponID heroArmyData.heroAppearanceId = v.HeroAppearanceId break end end return heroArmyData end function LevelFunction:OnIllusionDead(entity) end function LevelFunction:OnVisiblityChanged(entity, oldLv, oldGroup) -- body end function LevelFunction:OnApplicationPause(pause) end return LevelFunction