local EventSystem = require('Event/EventSystem') local Base = require("Battle/Logic/Level/LevelItemFactory/LevelItem") local ColliderManager = require("Battle/Logic/Level/ColliderManager") local LevelItem_Monster = class("LevelItem_Monster", Base) local BornCountEachFrame = 1 --限制每帧刷出怪物数量 local ConditionType = LuaConst.LevelConditionType local ActionType = LuaConst.LevelActionType local ActionFunc = {} local ActionCacheFunc = {} local math_min = math.min local string_isNilOrEmpty = string.isNilOrEmpty local FP = FP local EventID = EventID local LuaConst = LuaConst local tonumberV2 = FP.tonumberV2 local COUNT_CEIL_NUM = 1 local BornMode = --刷怪模式 { ReplenishNum = 0, -- 自动补全数量 IntervalTime = 1 -- 固定时间间隔 } local ExpEntity = require "Battle/Expression/ExpEntity/ExpEntity" --region --***************生命周期***************-- function LevelItem_Monster:initialize(data) Base.initialize(self,data) if data.ActiveDis == FP.Const.NegOne then self._colliderID = nil else self._colliderID = ColliderManager.CreateCircleCollider(data.ItemPos,data.ActiveDis) ColliderManager.AddEnterEvent(self._colliderID,self.TriggerBornMonster,self) end self._overrideBornPos = nil --怪物出生位置重写值 if not data.IsRandomByRandomPoolItem then self._totalWeight = FP.newV1(0) --怪物的总权重 for i, v in ipairs(data.WeightMonsterIDList) do self._totalWeight:Add(v.Weight) end end self._leftCount = 0 -- 元素剩余需要刷的怪物数量(endless模式下此字段无意义,不可使用) self._monsterDeadAll = false -- 怪物是否全部死亡,触发了死亡后根据计数修改该值 self._readyBornCount = 0 -- 备孕数量 self._monsterDeadNums = 0 --统计怪物死亡数量 self._monsterRemainNums = 0 -- 当前场上剩余怪物数量 self._allEntityDict = {} -- 所有出生的entity【注意:不要随意用pairs,会导致不一致的】todo:这是个不一致的隐患 self._bornCacheList = {} -- 准备出生的entity self._renderMonsterList = {} --当前所有的模型列表 self._actionCacheList = {} -- 缓存行为类型与参数 self._needHPPercent = nil -- (优化字段)是否需要发送血量事件 self._refreshTimer = FP.newV1(0) --刷新计时 self._bornTriggered = false -- 是否已经触发怪物出生 self._bornAgent = nil -- 怪物出生的刷新代理 self._minimapAgent = nil -- 小地图事件的代理 self._endless = self.ItemData.RefreshCount <= 0 -- 是否无尽刷怪模式 self._bornRenderMonster = self.ItemData.RenderToMonster -- 是否只出生模型,和策划对过,当前该字段影响整个元素的生命周期 self._coveredKillerID = nil self._context = { GetCoveredKillerID = function() return self._coveredKillerID end } self.isClientTest = nil end function LevelItem_Monster:SetOverrideBornPos(pos) self._overrideBornPos = pos end function LevelItem_Monster:SetOverrideMonsterID(monsterID) self._overrideMonsterID = monsterID end function LevelItem_Monster:OnActive() if not self.IsActive then self.IsActive = true self:TryCheckMonsterIsGroup() EventSystem.Regist(self.LevelItemID..EventID._EntityDead, self.OnMonsterDead, self) EventSystem.Regist(self.LevelItemID..EventID._EntityAIMessage, self.OnAIMessage, self) EventSystem.Regist(self.LevelItemID..EventID._EntityHPChanged, self.OnMonsterHPChanged, self) EventSystem.Regist(self.LevelItemID..EventID._EntityInteracted, self.OnMonsterInteracted, self) EventSystem.Regist(EventID.Level_DeleteMonster, self.OnLevelDeleteMonster, self) end local itemData = self.ItemData self._bornTriggered = false self._monsterDeadAll = false self._readyBornCount = itemData.Count self._leftCount = 0 self._refreshTimer:Reset(itemData.IsFirstDelay and itemData.RefreshDelayTime or 0) if not self._endless then -- gws 军团怪只刷出一个,但是表现模型数量为self.ItemData.RefreshCount个 local addLeftCount = self.isGroupMonster and 1 or itemData.RefreshCount * itemData.Count self._leftCount = self._leftCount + addLeftCount self:Dispatch(ConditionType.Monster_RemainNum,nil, addLeftCount) end if self._monsterRemainNums == 0 and itemData.UnbornRedPointType and itemData.UnbornRedPointType > 0 then EventSystem.Dispatch(EventID.UI_MiniMap_AddItem, itemData.LevelItemID, itemData) LevelItemManager.Global:Notify_AddItemOnMap(itemData.LevelItemID) if FP.Greater(self._refreshTimer, itemData.UnbornMapWaitTime) then self._minimapAgent = self.Tick_MinimapEvent else EventSystem.Dispatch(EventID.UI_MiniMap_CountDownItem, itemData.LevelItemID, FP.tonumberV1(self._refreshTimer)) end end self:ActiveTrigger() return true end function LevelItem_Monster:OnDeactive() if self.IsActive then self.IsActive = false EventSystem.UnRegist(self.LevelItemID..EventID._EntityDead,self.OnMonsterDead,self) EventSystem.UnRegist(self.LevelItemID..EventID._EntityAIMessage,self.OnAIMessage,self) EventSystem.UnRegist(self.LevelItemID..EventID._EntityHPChanged,self.OnMonsterHPChanged,self) EventSystem.UnRegist(self.LevelItemID..EventID._EntityInteracted, self.OnMonsterInteracted, self) EventSystem.UnRegist(EventID.Level_DeleteMonster, self.OnLevelDeleteMonster, self) end self._leftCount = 0 self:DeactiveTrigger() local itemData = self.ItemData if itemData.UnbornRedPointType and itemData.UnbornRedPointType > 0 then EventSystem.Dispatch(EventID.UI_MiniMap_DeleteItem, itemData.LevelItemID) LevelItemManager.Global:Notify_DeleteItemOnMap(itemData.LevelItemID) end self._minimapAgent = nil return true end function LevelItem_Monster:OnDestroy() Base.OnDestroy(self) self:DeleteAll(true) if self._colliderID then ColliderManager.DestroyCollider(self._colliderID) self._colliderID = nil end self._totalWeight = nil self._leftCount = nil self._monsterDeadAll = nil self._refreshTimer = nil self._readyBornCount = nil self._bornCacheList = nil self._allEntityDict = nil -- 所有出生的entity self._actionCacheList = nil self._needHPPercent = nil self._bornTriggered = nil self._endless = nil self._coveredKillerID = nil self._context = nil self._monsterDeadNums = nil self.isClientTest = nil end function LevelItem_Monster:OnUpdate() Base.OnUpdate(self) if #self._bornCacheList > 0 then -- 每帧刷出5个怪物 ;当时军团怪的时候,这个bornCacheList存储的军团怪的表现模型位置 if self._bornRenderMonster then self:BornRenderMonster() else self:BornMonster() end end if self._bornAgent then self:_bornAgent() end if self._minimapAgent then self:_minimapAgent() end end --endregion 生命周期 --region --***************事件相关***************-- function LevelItem_Monster:DoAction(actionType, match, param1, param2, context, param3) Base.DoAction(self, actionType,param1) local func = ActionFunc[actionType] if func then func(self,param1,param2,param3) end end function LevelItem_Monster:HandleCondition(conditionType,paramId,param1) Base.HandleCondition(conditionType,paramId,param1) if conditionType == ConditionType.Monster_RemainHPPct then self._needHPPercent = true end end function LevelItem_Monster:OnMonsterDead(entity, levelConditionIgnoreCount) if entity.components.record then local killer = entity.components.record.KillerEntity self._coveredKillerID = killer and killer:GetEntityID() or nil end self._monsterDeadNums = self._monsterDeadNums + COUNT_CEIL_NUM self._monsterRemainNums = self._monsterRemainNums - COUNT_CEIL_NUM self._allEntityDict[entity._logicInitData.EntityID] = nil if self.ItemData.Mode == BornMode.ReplenishNum then --自动补全模式刷怪 local readyCount = self._readyBornCount + COUNT_CEIL_NUM self._readyBornCount = self._endless and readyCount or math_min(math_min(self.ItemData.Count, self._leftCount), readyCount) end self._monsterDeadAll = not self._endless and self._monsterRemainNums <= 0 and self._leftCount <= 0 and #self._bornCacheList <= 0 local monsterID = entity._logicInitData.CharacterID local beCount = not entity.components.base.CurMonsterFeature.NotBeCount -- 如:建筑物 ,最好放在关卡元素中配置 if levelConditionIgnoreCount then beCount = false end if beCount then self:Dispatch(ConditionType.DeadNumber,self._context, monsterID, COUNT_CEIL_NUM) end self:Dispatch(ConditionType.Monster_AliveNum, nil, -COUNT_CEIL_NUM) if not self._endless then self:Dispatch(ConditionType.Monster_RemainNum, nil, -COUNT_CEIL_NUM) end if self._needHPPercent then local comp_Attr = entity.components.attr self:Dispatch(ConditionType.Monster_RemainHPPct, nil, entity, nil, -FP.tonumberV1(comp_Attr.hp)) end if beCount then LevelItemManager.Global:Notify_MonsterDead(entity) end if self._monsterDeadAll then self:Dispatch(ConditionType.Monster_DeadAll, self._context) if beCount then LevelItemManager.Global:Notify_MonsterItemDeadAll(entity) end EventSystem.Dispatch(EventID.BI_LevelMiddle,BIManager.BIType.LevelLayout,self.LevelItemID, 1, "Monster DeadAll") self:OnDeactive() end self._coveredKillerID = nil end function LevelItem_Monster:OnAIMessage(entity, aiID) local monsterID = entity._logicInitData.CharacterID self:Dispatch(ConditionType.ReceiveAI,nil, monsterID, aiID) LevelItemManager.Global:Notify_MonsterAIMessage(entity,aiID) end function LevelItem_Monster:OnMonsterHPChanged(entity,realDelta,value) if self._needHPPercent then self:Dispatch(ConditionType.Monster_RemainHPPct,nil,entity,nil,FP.tonumberV1(realDelta)) end LevelItemManager.Global:Notify_MonsterHPChanged(entity,realDelta) end function LevelItem_Monster:OnMonsterInteracted(entity, interactData) local monsterID = entity._logicInitData.CharacterID self:Dispatch(ConditionType.InteractNumber,nil, monsterID, COUNT_CEIL_NUM) LevelItemManager.Global:Notify_MonsterInteracted(entity, interactData) end function LevelItem_Monster:OnLevelDeleteMonster(monsterID) for i, v in ipairs(self.ItemData.WeightMonsterIDList) do if monsterID == v.MonsterID then self:ClearBorn() self:DeleteRenderMonster() break end end end --endregion 事件相关 --region --***************外部方法***************-- --[[ 重载元素数据(ItemData)的方法 为保证数据的一致性,将ItemData引用到新的表并写入重载数据,同时将原始ItemData设为新ItemData的元表 重载需要保证数据格式一致,重载字段:WeightMonsterIDList ]]-- ---@param data 对应元素的重载数据 function LevelItem_Monster:OverrideData(data) if #data > 0 then self._totalWeight:Reset() local newItemData = { WeightMonsterIDList = {}, IsRandomByRandomPoolItem = false} -- IsRandomByRandomPoolItem: 当需要覆盖数据时,优先用覆盖数据 local weight = 100/#data for i,v in ipairs(data) do newItemData.WeightMonsterIDList[i] = { MonsterID = v, Weight = FP.newV1(weight) } self._totalWeight:Add(weight) end self.ItemData.__index = self.ItemData setmetatable(newItemData, self.ItemData) self.ItemData = newItemData end end function LevelItem_Monster:CheckMonsterDeadAll() return self._monsterDeadAll end function LevelItem_Monster:CheckTriggered() return self.IsActive and self._bornTriggered end function LevelItem_Monster:ClearBorn() --如果调用了ClearBorn,停止刷怪Tick self._bornAgent = nil self._leftCount = 0 self._readyBornCount = 0 for i = #self._bornCacheList, 1, -1 do self._bornCacheList[i] = nil end for i = #self._actionCacheList, 1, -1 do self._actionCacheList[i] = nil end end function LevelItem_Monster:GetMonsterEntity() local count = 0 for _, entity in pairs(self._allEntityDict) do count = count + 1 end if count > 1 then logging.error(LTAG.ERROR, "HYW : 致命错误,试图访问有多个怪物的怪物元素的 GetMonsterEntity 方法, 这会导致不一致!") end for _, entity in pairs(self._allEntityDict) do return entity end return nil end function LevelItem_Monster:GetAllMonsterEntity() return self._allEntityDict end function LevelItem_Monster:GetMinimapTimer() if not FP.Greater(self._refreshTimer, self.ItemData.UnbornMapWaitTime) then return FP.tonumberV1(self._refreshTimer) end end function LevelItem_Monster:GetDeadNumber() return self._monsterDeadNums end function LevelItem_Monster:GetMonsterID(index) return self.ItemData.WeightMonsterIDList[index].MonsterID end --endregion 外部方法 --region --***************内部方法***************-- --默认执行的操作:刷出怪物 function LevelItem_Monster:TriggerBornMonster() self._bornTriggered = true self:DeactiveTrigger() self:StartBornMonster() end function LevelItem_Monster:ActiveTrigger() if self._colliderID then ColliderManager.ActiveCollider(self._colliderID, LogicUpdateManager.ValidSyncEntityIter) else self:TriggerBornMonster() end end function LevelItem_Monster:DeactiveTrigger() if self._colliderID then ColliderManager.DeActiveCollider(self._colliderID) end end function LevelItem_Monster:StartBornMonster() if not self._endless and self._leftCount <= 0 then return end local itemData = self.ItemData if itemData.Mode == BornMode.ReplenishNum then self._bornAgent = self.BornTick_ReplenishNum elseif itemData.Mode == BornMode.IntervalTime then self._bornAgent = self.BornTick_IntervalTime end if self._refreshTimer <= FP.Const.Zero then self._refreshTimer:Reset(itemData.RefreshDelayTime) self:BornMonsterCache() end end function LevelItem_Monster:StopBornMonster(keepTimer) if keepTimer then self._bornAgent = self.Tick_UpdateTime else local itemData = self.ItemData self._refreshTimer:Reset(itemData.IsFirstDelay and itemData.RefreshDelayTime or 0) self._bornAgent = nil end end function LevelItem_Monster:BornTick_ReplenishNum() if self._readyBornCount > 0 then -- 代表此时需要补全 self._refreshTimer:Sub(LuaConst.TimeSpan) if self._refreshTimer <= FP.Const.Zero then self._refreshTimer:Reset(self.ItemData.RefreshDelayTime) local overReplenishNum = 0 local limitNum = self.ItemData.ReplenishNumLimit if limitNum and limitNum > 0 and self._readyBornCount > limitNum then -- 超过了一次补充的兵员量 overReplenishNum = self._readyBornCount - limitNum self._readyBornCount = limitNum end self:BornMonsterCache() if overReplenishNum > 0 then self._readyBornCount = overReplenishNum end end end end function LevelItem_Monster:BornTick_IntervalTime() self._refreshTimer:Sub(LuaConst.TimeSpan) if self._refreshTimer <= FP.Const.Zero then self._refreshTimer:Reset(self.ItemData.RefreshDelayTime) self._readyBornCount = self.ItemData.Count self:BornMonsterCache() end end function LevelItem_Monster:Tick_UpdateTime() if self._refreshTimer > FP.Const.Zero then self._refreshTimer:Sub(LuaConst.TimeSpan) end end function LevelItem_Monster:Tick_MinimapEvent() if FP.Greater(self._refreshTimer, self.ItemData.UnbornMapWaitTime) then return end EventSystem.Dispatch(EventID.UI_MiniMap_CountDownItem, self.LevelItemID, self.ItemData.UnbornMapWaitTime) self._minimapAgent = nil end --怪物出生缓存 function LevelItem_Monster:BornMonsterCache() if not LevelItemManager.GetDontBornMonsterFlag(self.LevelItemID) then local itemData = self.ItemData local itemPosV2 = self._overrideBornPos or itemData.ItemPos local forwardV2 = itemData.ItemDir local rightV2 = FP.V2.GetRightByForward(forwardV2) local count = self._readyBornCount if self.isGroupMonster then -- 军团怪只刷出一个,但是要多个模型 ,所以可以存储在_bornCacheList中 count = self.ItemData.Count end local layout = itemData.LayoutType if layout == LuaConst.EntityLayoutType.OriginPos then for i = 1,count do local positionV2 = itemPosV2 self:AddMonsterCache(positionV2,forwardV2) end elseif layout == LuaConst.EntityLayoutType.Random_Circle then for i = 1,count do local radian = LogicRandom.GetRangeValue(nil, FP.Const.Zero, FP.Const.PI2 ,31) local radius = LogicRandom.GetRangeValue(nil, FP.Const.Zero, itemData.Radius ,32) local offX = radius * FP.Math.Cos(radian) local offZ = radius * FP.Math.Sin(radian) local positionV2 = itemPosV2 + FP.newV2(offX,offZ) self:AddMonsterCache(positionV2,forwardV2) end elseif layout == LuaConst.EntityLayoutType.Random_Square then for i= 1,count do local halfLength = itemData.Length / 2 local halfWidth = itemData.Width / 2 local randLength = LogicRandom.GetRangeValue(nil, -halfLength,halfLength ,33) local randWidth = LogicRandom.GetRangeValue(nil, -halfWidth, halfWidth ,34) local positionV2 = itemPosV2 + rightV2 * randLength + forwardV2 * randWidth self:AddMonsterCache(positionV2,forwardV2) end elseif layout == LuaConst.EntityLayoutType.Matrix then local orgOffsetCol = itemData.Gap * (itemData.Col - 1) / 2 local orgOffsetRow = itemData.Gap * (itemData.Row - 1) / 2 local orgPosV2 = itemPosV2 - rightV2 * orgOffsetCol - forwardV2 * orgOffsetRow local index = 0 for i=0, itemData.Col - 1 do for j = 0, itemData.Row - 1 do local positionV2 = orgPosV2 + rightV2 * i * itemData.Gap + forwardV2 * j * itemData.Gap self:AddMonsterCache(positionV2,forwardV2) index = index + 1 if index >= count then break end end if index >= count then break end end end if not self._endless then self._leftCount = self._leftCount - self._readyBornCount if self._leftCount <= 0 then self._bornAgent = nil end end self._readyBornCount = 0 if itemData.UnbornRedPointType and itemData.UnbornRedPointType > 0 then EventSystem.Dispatch(EventID.UI_MiniMap_DeleteItem, itemData.LevelItemID) LevelItemManager.Global:Notify_DeleteItemOnMap(itemData.LevelItemID) end end end function LevelItem_Monster:AddMonsterCache(positionV2,forwardV2) local cacheData = {} cacheData.forward = forwardV2 cacheData.position = positionV2 table.insert(self._bornCacheList,cacheData) end function LevelItem_Monster:RegistEntity(entity) if self.ItemData then self._allEntityDict[entity.EntityID] = entity if entity and entity:IsAlive() then self._monsterDeadAll = false end entity:InitEventOfBornItemID(self.LevelItemID) else logging.error(LTAG.ERROR, "HYW : Error RegistEntity when self.ItemData == nil @LevelItem_Monster:RegistEntity") end end function LevelItem_Monster:UnregistEntity(entity) self._allEntityDict[entity.EntityID] = nil end function LevelItem_Monster:GetAliveMonsterCount() local count = 0 for k,v in pairs(self._allEntityDict) do --pairs safe 数据统计对顺序不敏感 if v:IsAlive() then count = count + 1 end end return count end --怪物出生 function LevelItem_Monster:BornMonster() if not LevelItemManager.GetDontBornMonsterFlag(self.LevelItemID) then if self.isGroupMonster then -- 军团怪只刷出一个,但是要多个模型 ,模型位置存储在_bornCacheList中 self:BornGroupMonster() return end local count = math.min(BornCountEachFrame,#self._bornCacheList) for i = 1, count do local data = table.remove(self._bornCacheList,1) -- self._bornCacheList[1] local monsterID = self._overrideMonsterID or self:GetRandomMonsterID() EntityFactory.SetType_ID(LuaConst.CharacterType.Monster, monsterID) EntityFactory.SetPos_Dir(data.position, data.forward) EntityFactory.SetParams("BornItemID", self.LevelItemID) EntityFactory.SetParams("IsTest", self.isClientTest) local entity = EntityFactory.DoCreateEntity() self._allEntityDict[entity._logicInitData.EntityID] = entity self:HandleActionCache(entity) self._monsterRemainNums = self._monsterRemainNums + 1 if self._needHPPercent then local comp_Attr = entity.components.attr self:Dispatch(ConditionType.Monster_RemainHPPct,nil,entity,FP.tonumberV1(comp_Attr:MaxHP()),FP.tonumberV1(comp_Attr.hp)) end self:Dispatch(ConditionType.Monster_AliveNum,nil, COUNT_CEIL_NUM) local notBeCount = entity.components.base.CurMonsterFeature.NotBeCount -- 如:建筑物 if not notBeCount then LevelItemManager.Global:Notify_MonsterBorn(entity) end end --碰撞优先级 if #self._bornCacheList == 0 then self:SetGridData() end end end function LevelItem_Monster:BornRenderMonster() if not LevelItemManager.GetDontBornMonsterFlag(self.LevelItemID) then local count = math.min(BornCountEachFrame,#self._bornCacheList) for i = 1, count do local data = table.remove(self._bornCacheList,1) local monsterID = self:GetRandomMonsterID() local monsterData = DataSheetManager.MonsterData:GetDataByID(monsterID) local modelData = DataSheetManager.MonsterModelData:GetDataByID(monsterData.ModelID) local featureData = DataSheetManager.MonsterFeatureData:GetDataByID(monsterData.MonsterType) local layer = self:GetMonsterLayer(monsterData.Camp,featureData.IsBossLayer) --生成阻挡 --RenderMonster暂时不支持旋转挤压 LuaShoveManager.SetGridDataByPriorityNew(modelData.ShoveDiameterX,modelData.ShoveDiameterZ, 10000, data.position, true, true) local px, pz = tonumberV2(data.position) local dx, dz = tonumberV2(data.forward) local renderEntity = ExpEntity.LoadRenderEntity(modelData, true, px, pz, dx, dz, not LevelManager.IsNoBattleLevel(), layer, monsterData.Camp, featureData.VisualPriority) local renderMonster = { MonsterID = monsterID, TransData = data, ModelData = modelData, RenderEntity = renderEntity } table.insert(self._renderMonsterList,renderMonster) end end end function LevelItem_Monster:DeleteAll(unloadDirectly, levelConditionIgnoreCount) self:ClearBorn() self:DeleteRenderMonster() if unloadDirectly then for _,entity in pairs(self._allEntityDict) do if not entity.hasUnload then SystemManager.OnUnload(entity,true) --else --正常情况,在 调用过 LevelItem_Monster:RegistEntity 后,且 LevelItem_Monster 没有 Active 时,RegistEntity的entity参数可能已经被Unload过了,且无法从self._allEntityDict里删除,这里就会 entity.hasUnload 为true end self._monsterRemainNums = self._monsterRemainNums - COUNT_CEIL_NUM self._allEntityDict[_] = nil end else for _,entity in pairs(self._allEntityDict) do if entity:IsAlive() then SystemManager.DeadQuit(entity, levelConditionIgnoreCount) end end end end function LevelItem_Monster:DeleteRenderMonster() for i = 1,#self._renderMonsterList do local renderMonster = self._renderMonsterList[i] ExpSystemManager.UnloadRenderEntity(renderMonster.RenderEntity, false) --取消阻挡 --renderMonster直接取消,不用withRotate版本 LuaShoveManager.ReSetGridDataByPriorityNew(renderMonster.ModelData.ShoveDiameterX,renderMonster.ModelData.ShoveDiameterZ, renderMonster.TransData.position) self._renderMonsterList[i] = nil end end function LevelItem_Monster:DeleteNumber(number) local unborn = 0 if not self._endless then unborn = number - self._monsterRemainNums if unborn > self._leftCount then unborn = self._leftCount end self._leftCount = self._leftCount - unborn end local index = 0 if unborn <= 0 then --杀死数量少于场上剩余数量时 for _,entity in pairs(self._allEntityDict) do if index >= number then break end if entity:IsAlive() then SystemManager.DeadQuit(entity) index = index + 1 end end else --杀死数量多于场上剩余数量时 for _,entity in pairs(self._allEntityDict) do if entity:IsAlive() then SystemManager.DeadQuit(entity) end end end end function LevelItem_Monster:ConvertRenderToMonster() self._bornRenderMonster = false while true do if LevelItemManager.GetDontBornMonsterFlag(self.LevelItemID) then break end local renderMonster = table.remove(self._renderMonsterList) --renderEntity列表 if not renderMonster then break end --取消阻挡 --renderMonster直接取消,不用withRotate版本 LuaShoveManager.ReSetGridDataByPriorityNew(renderMonster.ModelData.ShoveDiameterX, renderMonster.ModelData.ShoveDiameterZ,renderMonster.TransData.position) EntityFactory.SetPos_Dir(renderMonster.TransData.position, renderMonster.TransData.forward) EntityFactory.SetType_ID(LuaConst.CharacterType.Monster, self._overrideMonsterID or renderMonster.MonsterID) EntityFactory.SetParams("BornItemID", self.LevelItemID, "RenderEntity", renderMonster.RenderEntity) local entity = EntityFactory.DoCreateEntity() self._allEntityDict[entity._logicInitData.EntityID] = entity self:HandleActionCache(entity) self._monsterRemainNums = self._monsterRemainNums + 1 if self._needHPPercent then local comp_Attr = entity.components.attr self:Dispatch(ConditionType.Monster_RemainHPPct,nil,entity,FP.tonumberV1(comp_Attr:MaxHP()),FP.tonumberV1(comp_Attr.hp)) end local notBeCount = entity.components.base.CurMonsterFeature.NotBeCount -- 如:建筑物 if not notBeCount then LevelItemManager.Global:Notify_MonsterBorn(entity) end end self:SetGridData() end function LevelItem_Monster:GetRandomMonsterID() local itemData = self.ItemData if itemData.IsRandomByRandomPoolItem then local randomPoolItem = LevelItemManager.GetItemByID(itemData.RandomPoolItemID) local result = randomPoolItem:GetRandomValue() return result else local random = LogicRandom.GetRangeValue(nil, FP.Const.Zero, self._totalWeight ,35) for i,v in ipairs(self.ItemData.WeightMonsterIDList) do random:Sub(v.Weight) if FP.LessEquals(random, 0) then return v.MonsterID end end end end function LevelItem_Monster:GetMonsterLayer(camp, isBossLayer) local isFriendCamp = not CampManager.IsEnemyCamp(camp,LevelPlayerManager.MainEntityCampID) if isBossLayer then return isFriendCamp and LuaConst.Layer.Boss_Friend or LuaConst.Layer.Boss_Enemy else return isFriendCamp and LuaConst.Layer.Soldier_Friend or LuaConst.Layer.Soldier_Enemy end end --TODO 这里需要优化 function LevelItem_Monster:SetGridData() for _,entity in pairs(self._allEntityDict) do local comp_Move = entity.components.move local priority = comp_Move.ShovePriority if priority > 50 then LuaShoveManager.SetGridDataByPriorityOfEntity(entity,priority,true,false) end end for _,entity in pairs(self._allEntityDict) do local comp_Move = entity.components.move local priority = comp_Move.ShovePriority if priority > 50 then LuaShoveManager.SetGridDataByPriorityOfEntity(entity,priority,false,true) end end end --endregion 内部方法 --region --***************行为方法***************-- ActionFunc[ActionType.Monster_Active] = function(self, param1, param2, param3) if param1 then self:DeleteAll(true) end if param3 then local item = LevelItemManager.GetItemByID(param3, LuaConst.LevelItemType.Monster) if item then if item._monsterRemainNums == 0 then self:OnActive() end else self:OnActive() logging.error(LTAG.ERROR, "HYW : 关卡策划注意 开始刷怪行为 没有找到指定的怪物元素, ID : ", param3) end else self:OnActive() end end ActionFunc[ActionType.Monster_DeleteNum] = function(self,param1) self:DeleteNumber(param1) end ActionFunc[ActionType.Monster_RenderToMonster] = function(self) self:ConvertRenderToMonster() end ActionFunc[ActionType.Monster_Suspend] = function(self, param1) if param1 then if self.IsActive then -- 暂停 --keepTimer(bool) 先默认通过模式控制,后面有需求可以改成配置 local keepTimer = self.ItemData.Mode == BornMode.IntervalTime if self._bornTriggered then self:StopBornMonster(keepTimer) else self:DeactiveTrigger() end end else -- 恢复 if not self.IsActive then --如果挂起恢复未激活的刷怪元素,则直接激活元素 self:OnActive() else --如果挂起恢复已经激活的刷怪元素,则只需恢复状态 -- 恢复时重置下数量(补全模式下不需要重置) if self.ItemData.Mode == BornMode.IntervalTime then self._readyBornCount = self.ItemData.Count end if self._bornTriggered then self:StartBornMonster() else self:ActiveTrigger() end end end end --**** 通用行为方法 ****-- ActionFunc[ActionType.HandleBuff] = function(self, param1, param2, match) if not self.IsActive then return end if param1 then for _,entity in pairs(self._allEntityDict) do SystemManager.AddBuff(entity, param2, nil, nil, nil) end self:AddActionCache(ActionType.HandleBuff, param2, false) else for _,entity in pairs(self._allEntityDict) do SystemManager.DeleteBuff(entity, param2, nil) end self:RemoveActionCache(ActionType.HandleBuff, param2, false) end end -- param1 : dialogueID , param2 : random number ActionFunc[ActionType.ShowBubble] = function(self, param1, param2, match) if not self.IsActive or param2 <= 0 then return end --为了简化和保证效果,做成分组随机,一组随机一个 local func = SystemManager.ShowBubbleDialogue local interval = math.floor(self._monsterRemainNums / param2) local leftTimes = param2 local index = 1 local randomValue = math.random(1,interval) for _, entity in pairs(self._allEntityDict) do if index > interval then index = 1 randomValue = math.random(1,interval) end if index == randomValue then func(entity,param1) leftTimes = leftTimes - 1 if leftTimes == 0 then break end end index = index + 1 end end ActionFunc[ActionType.NotifyAI] = function(self, param1, param2, match) if not self.IsActive then return end for _,entity in pairs(self._allEntityDict) do SystemManager.OnNotifyAI(entity, param1) end self:AddActionCache(ActionType.NotifyAI,param1,false) end ActionFunc[ActionType.LockAI] = function(self, param1, param2, match) if not self.IsActive then return end for _,entity in pairs(self._allEntityDict) do SystemManager.OnLockAI(entity, param1) end if param1 then self:AddActionCache(ActionType.LockAI,param1,true) else self:RemoveActionCache(ActionType.LockAI,nil,true) end end ActionFunc[ActionType.Delete] = function(self, param1, param2, match) local levelConditionIgnoreCount = param1 == false self:DeleteAll(false, levelConditionIgnoreCount) -- param1 :关卡条件是否计数 end ActionFunc[ActionType.Rout] = function(self, param1, param2, match) self:ClearBorn() if not self.IsActive then return end for _,entity in pairs(self._allEntityDict) do SystemManager.OnNotifyAI(entity, LuaConst.LevelRoutAIID) end end ActionFunc[ActionType.ShowObject] = function(self, param1, param2, match) if not self.IsActive then return end for _,entity in pairs(self._allEntityDict) do SystemManager.OnShowObject(entity, param1, param2) end end --endregion 行为方法 --region --***************缓存行为方法***************-- function LevelItem_Monster:AddActionCache(type,param1,cover) if cover then for i = #self._actionCacheList, 1, -1 do local value = self._actionCacheList[i] if value[1] == type then table.remove(self._actionCacheList,i) break end end end table.insert(self._actionCacheList, {type,param1}) end function LevelItem_Monster:RemoveActionCache(type,param1,cover) if cover then for i = #self._actionCacheList, 1, -1 do local value = self._actionCacheList[i] if value[1] == type then table.remove(self._actionCacheList,i) break end end else for i = #self._actionCacheList, 1, -1 do local value = self._actionCacheList[i] if value[1] == type and value[2] == param1 then table.remove(self._actionCacheList,i) end end end end function LevelItem_Monster:HandleActionCache(entity) for i,v in ipairs(self._actionCacheList) do local func = ActionCacheFunc[v[1]] if func then func(entity,v[2]) end end end ActionCacheFunc[ActionType.LockAI] = SystemManager.OnLockAI ActionCacheFunc[ActionType.NotifyAI] = SystemManager.OnNotifyAI ActionCacheFunc[ActionType.HandleBuff] = function(entity, buffID) SystemManager.AddBuff(entity, buffID, nil, nil, nil) end --endregion 缓存行为方法 ----以下代码是策划(李宜伟)需求:当刷的怪物是一个军团怪的时候(Feature的IsGroupBorn),强制这个军团只能是一个,但是军团怪的模型数量是多个 ----当怪物是一个军团的时候 ↓↓↓↓↓ function LevelItem_Monster:TryCheckMonsterIsGroup() if self.hasCheckMonsterIsGroup then return end self.hasCheckMonsterIsGroup = true local monsterID local monsterData local featureData -- 如果是从随机池元素中拿数据,则先检查下随机池是否是军团怪配置,然后再真正的从随机池取数据 if self.ItemData.IsRandomByRandomPoolItem then local randomPoolItem = LevelItemManager.GetItemByID(self.ItemData.RandomPoolItemID) monsterID = randomPoolItem:GetFirstValue() monsterData = DataSheetManager.MonsterData:GetDataByID(monsterID) if monsterData == nil then logging.error(LTAG.ERROR, "【李宜伟】 :关卡元素找不到怪 :元素id =", self.ItemData.LevelItemID, ",怪物id =", monsterID) end featureData = DataSheetManager.MonsterFeatureData:GetDataByID(monsterData.MonsterType) if not featureData.IsGroupBorn then self.isGroupMonster = false return --- 检测到了是非军团怪,则不用随机了 end end monsterID = self:GetRandomMonsterID() ---可以这样判断的前提是 :配置的权重怪物必须全部是军团怪,不支持 军团怪和普通怪混合配置 monsterData = DataSheetManager.MonsterData:GetDataByID(monsterID) if monsterData == nil then logging.error(LTAG.ERROR, "【李宜伟】 :关卡元素找不到怪 :元素id =", self.ItemData.LevelItemID, ",怪物id =", monsterID) end --local modelData = DataSheetManager.MonsterModelData:GetDataByID(monsterData.ModelID) featureData = DataSheetManager.MonsterFeatureData:GetDataByID(monsterData.MonsterType) if featureData == nil then logging.error(LTAG.ERROR, "【李宜伟】 :关卡元素找不到怪的featureData :元素id =", self.ItemData.LevelItemID, ",怪物id =", monsterID) end if featureData.IsGroupBorn then self.isGroupMonster = true -- 标记次元素刷出的怪物是军团怪,需要记录军团怪的信息 self.groupMonsterID = monsterID self.groupMonsterData = monsterData else self.isGroupMonster = false end end function LevelItem_Monster:BornGroupMonster() if not LevelItemManager.GetDontBornMonsterFlag(self.LevelItemID) then --self._bornCacheList local data = self.ItemData local position = self._overrideBornPos or data.ItemPos local forward = data.ItemDir EntityFactory.SetType_ID(LuaConst.CharacterType.Monster, self._overrideMonsterID or self.groupMonsterID) EntityFactory.SetPos_Dir(position, forward) EntityFactory.SetParams("BornItemID", self.LevelItemID) EntityFactory.SetGroupMonsterModelPos_Dir(self._bornCacheList) local entity = EntityFactory.DoCreateEntity() entity:SetGs(LevelManager.GetLevelGs()) self._allEntityDict[entity._logicInitData.EntityID] = entity self:HandleActionCache(entity) self._monsterRemainNums = self._monsterRemainNums + 1 if self._needHPPercent then local comp_Attr = entity.components.attr self:Dispatch(ConditionType.Monster_RemainHPPct,nil,entity,FP.tonumberV1(comp_Attr:MaxHP()),FP.tonumberV1(comp_Attr.hp)) end self:Dispatch(ConditionType.Monster_AliveNum,nil, COUNT_CEIL_NUM) local notBeCount = entity.components.base.CurMonsterFeature.NotBeCount -- 如:建筑物 if not notBeCount then LevelItemManager.Global:Notify_MonsterBorn(entity) end --碰撞优先级 self:SetGridData() table.clearArray(self._bornCacheList) end end function LevelItem_Monster:SetClientTest(isTest) self.isClientTest = isTest; end ----当怪物是一个军团的时候 ↑↑↑↑↑ return LevelItem_Monster