--移动组件 local ExpEffectSystem = {} local private = { _buffEffectPlayID = 0, _buffEffectPlayData = {}, --{buffEffectPlayID = path, expEntity = { PsCountInfo = {path = playCount}, PsPlayInfo = {path = psPlayID} }} _waitingPlayEffectEntityDic = {}, -- 统计有播放特效队里的expEntity ;为了省cpu才加的这个 _repeatPlayLightEffectDic = {}, -- 需要每隔n秒时间就要播放一次的光效{ [expEntity] = table } 人物可能会装备多个循环光效所以设置为表,表里每个元素的结构:{ lightEffectData:光效数据Data, cdTimer:播放间隔} _hookPlayID = 0, _hookData = {}, --每个钩子子弹特效的播放信息 {[hookPlayID] = {EffectID = effectID, EffectPath = effectPath, HookStart = hookStart, HookTarget = hookTarget}} _hookEntityData = {}, --记录每个钩子链接的expEntity播放信息 {expEntity = {[1] = hookPlayID1, [2] = hookPlayID2 ... }} } local BattleLightEffectPathDic = {[202004] = ConstAssetPathConfig.RelativeEffect.Func_SpecLightEffect_Relative} -- 特殊的光效播放替换资源 。 local TownCartManager = require "Battle/Logic/MainLoop/SyncMoveManager/TownCartManager" local EFFECTPATH = ConstAssetPathConfig.PsGroupPath local QueueEffectInterval = FP.tonumberV1( DataSheetManager.ConstData:GetDataByID("QueueEffectInterval").FloatValue ) local LuaConst=LuaConst function ExpEffectSystem.OnAttackEnter(expEntity, batchEffect) local entity = expEntity.Entity local skillResource = entity.components.skillBase:GetCurSkillResource() if batchEffect then SystemManager.OnReceivePsEffect(entity, skillResource) else SystemManager.ShowPSEffect(entity, skillResource) end end function ExpEffectSystem.OnAttackQuit(expEntity) local comp_SkillBase = expEntity.Entity.components.skillBase if comp_SkillBase.CurSkillData then local skillResource = comp_SkillBase:GetCurSkillResource() if comp_SkillBase.CurSkillInterrupted == LuaConst.SkillInterruptType.BreakEffect then SystemManager.OnBreakPSEffect(expEntity.Entity,skillResource) elseif comp_SkillBase.CurSkillInterrupted == LuaConst.SkillInterruptType.StopEffect then SystemManager.StopPSEffect(expEntity.Entity,skillResource) end end end function ExpEffectSystem.OnInterruptSkill(expEntity) local skillResource = expEntity.Entity.components.skillBase:GetCurSkillResource() SystemManager.StopPSEffect(expEntity.Entity, skillResource) end function ExpEffectSystem.OnWinBattle(expEntity,winEffect) expEntity:ClearDynamicEffects() expEntity:ClearActorPsEffectRecord() expEntity:PlayHeroLightEffect(expEntity.Entity._logicInitData.LightEffectIDs, true) expEntity:PlayCurModelEffect(expEntity.Entity.components.replace:GetCurExpModelData()) local logicInitData = expEntity.Entity._logicInitData local heroId = expEntity.Entity:GetCharacterID() expEntity:ChangeWeaponEffect(heroId, logicInitData.MagicWeaponID, logicInitData.IsActiveMagicWeapon, logicInitData.WeaponBreak) SystemManager.ShowPSEffect(expEntity.Entity,winEffect, true) end function ExpEffectSystem.OnRideHorse(expEntity,horseEffect) local isPlayerSelf = expEntity.Entity._logicInitData.IsPlayerSelf if isPlayerSelf then expEntity:PlayHorseEffect(horseEffect) end end function ExpEffectSystem.OnOffHorse(expEntity,horseEffect) local isPlayerSelf = expEntity.Entity._logicInitData.IsPlayerSelf if isPlayerSelf then SystemManager.PlayPsEffect(horseEffect,expEntity.Entity) end end function ExpEffectSystem.PlayHookEffect(expEntity, effectPath) local layer = expEntity.components.model.Layer local effectPlayID = 0 if not expEntity.RenderEntity:IsDummy() then if GameAppInfo.IsVersionGreaterThan119000() then local effectAsPlayerSelf = ExpUpdateManager.IsEffectAsPlayerSelf(expEntity.Entity) effectPlayID = AuxUtil.PlayPsEffect(effectPath, expEntity.RenderEntity, layer, effectAsPlayerSelf, not effectAsPlayerSelf) else effectPlayID = AuxUtil.PlayPsEffect(effectPath, expEntity.RenderEntity, layer) end end private._hookPlayID = private._hookPlayID + 1 private._hookData[private._hookPlayID] = { EffectID = effectPlayID, EffectPath = effectPath, HookStart = expEntity, HookTarget = nil } if private._hookEntityData[expEntity] == nil then private._hookEntityData[expEntity] = {} end table.insert(private._hookEntityData[expEntity], private._hookPlayID) return private._hookPlayID end function ExpEffectSystem.TryHookEntity(hookEffectID, hookTargetExpEntity) if private._hookEntityData[hookTargetExpEntity] == nil then private._hookEntityData[hookTargetExpEntity] = {} end table.insert(private._hookEntityData[hookTargetExpEntity], hookEffectID) local hookData = private._hookData[hookEffectID] if hookData ~= nil then hookData.HookTarget = hookTargetExpEntity if hookData.EffectID == 0 and hookData.HookStart ~= nil then local layer = hookData.HookStart.components.model.Layer if GameAppInfo.IsVersionGreaterThan119000() then local effectAsPlayerSelf = ExpUpdateManager.IsEffectAsPlayerSelf(hookData.HookStart.Entity) or (hookData.HookTarget and ExpUpdateManager.IsEffectAsPlayerSelf(hookData.HookTarget.Entity)) hookData.EffectID = AuxUtil.PlayPsEffect(hookData.EffectPath, hookData.HookStart.RenderEntity, layer, effectAsPlayerSelf, not effectAsPlayerSelf) else hookData.EffectID = AuxUtil.PlayPsEffect(hookData.EffectPath, hookData.HookStart.RenderEntity, layer) end end if hookData.EffectID ~= 0 and hookData.HookTarget ~= nil then AuxUtil.PsHandleEntityRelated(hookData.EffectID, hookData.HookTarget.RenderEntity) end end end function ExpEffectSystem.OnConvertRenderVisual(expEntity, isVisible) if isVisible then if expEntity and not expEntity.dontShowVisualInst then ExpEffectSystem.ReplayHookEffect(expEntity) end else ExpEffectSystem.HideHookEffect(expEntity) end end function ExpEffectSystem.ReplayHookEffect(expEntity) local localPlayIDs if private._hookEntityData[expEntity] ~= nil then localPlayIDs = private._hookEntityData[expEntity] end if localPlayIDs ~= nil then for k, v in ipairs(localPlayIDs) do local hookData = private._hookData[v] if hookData.EffectID == 0 and hookData.HookStart ~= nil then local layer = hookData.HookStart.components.model.Layer if GameAppInfo.IsVersionGreaterThan119000() then local effectAsPlayerSelf = ExpUpdateManager.IsEffectAsPlayerSelf(hookData.HookStart.Entity) or (hookData.HookTarget and ExpUpdateManager.IsEffectAsPlayerSelf(hookData.HookTarget.Entity)) hookData.EffectID = AuxUtil.PlayPsEffect(hookData.EffectPath, hookData.HookStart.RenderEntity, layer, effectAsPlayerSelf, not effectAsPlayerSelf) else hookData.EffectID = AuxUtil.PlayPsEffect(hookData.EffectPath, hookData.HookStart.RenderEntity, layer) end end if hookData.EffectID ~= 0 and hookData.HookTarget ~= nil then AuxUtil.PsHandleEntityRelated(hookData.EffectID, hookData.HookTarget.RenderEntity) end end end end function ExpEffectSystem.HideHookEffect(expEntity) local localPlayIDs if private._hookEntityData[expEntity] ~= nil then localPlayIDs = private._hookEntityData[expEntity] end if localPlayIDs ~= nil then for k, v in ipairs(localPlayIDs) do local hookData = private._hookData[v] --如果钩子目标变成Dummy,检测钩子发射者,如果发射者为是Dummy,则停掉钩子特效 if hookData.HookTarget == expEntity and (hookData.HookStart == nil or hookData.HookStart.RenderEntity == nil or hookData.HookStart.RenderEntity:IsDummy()) then hookData.EffectID = AuxUtil.StopPsEffect(hookData.EffectID) hookData.EffectID = 0 end --从Visual转换到Dummy,C#会停掉所有发射者的特效 if hookData.HookStart == expEntity then --如果是钩子发射者变成Dummy,目标不是Dummy状态,需要重新播放钩子特效,并链接两端 if hookData.HookTarget ~= nil and hookData.HookTarget.RenderEntity ~= nil and not hookData.HookTarget.RenderEntity:IsDummy() then AuxUtil.StopPsEffect(hookData.EffectID) local layer = hookData.HookStart.components.model.Layer if GameAppInfo.IsVersionGreaterThan119000() then local effectAsPlayerSelf = ExpUpdateManager.IsEffectAsPlayerSelf(hookData.HookStart.Entity) or (hookData.HookTarget and ExpUpdateManager.IsEffectAsPlayerSelf(hookData.HookTarget.Entity)) hookData.EffectID = AuxUtil.PlayPsEffect(hookData.EffectPath, hookData.HookStart.RenderEntity, layer, effectAsPlayerSelf, not effectAsPlayerSelf) else hookData.EffectID = AuxUtil.PlayPsEffect(hookData.EffectPath, hookData.HookStart.RenderEntity, layer) end if hookData.EffectID ~= 0 then AuxUtil.PsHandleEntityRelated(hookData.EffectID, hookData.HookTarget.RenderEntity) end else --钩子特效被剔除系统,从C#关掉了 hookData.EffectID = 0 end end end end end function ExpEffectSystem.StopHookEffect(effectPlayID) if private._hookData[effectPlayID] ~= nil then local data = private._hookData[effectPlayID] if data.EffectID ~= 0 then if data.HookTarget ~= nil then AuxUtil.PsHandleEntityUnrelated(data.EffectID, data.HookTarget.RenderEntity) end AuxUtil.StopPsEffect(data.EffectID) end private._hookData[effectPlayID] = nil if data.HookStart ~= nil then local hookEntityIDs = private._hookEntityData[data.HookStart] if hookEntityIDs ~= nil then local count = #hookEntityIDs for i = count, 1, -1 do if hookEntityIDs[i] == effectPlayID then table.remove(hookEntityIDs, i) end end if not next(hookEntityIDs) then private._hookEntityData[data.HookStart] = nil end end end if data.HookTarget ~= nil then local hookEntityIDs = private._hookEntityData[data.HookTarget] if hookEntityIDs ~= nil then local count = #hookEntityIDs for i = count, 1, -1 do if hookEntityIDs[i] == effectPlayID then table.remove(hookEntityIDs, i) end end if not next(hookEntityIDs) then private._hookEntityData[data.HookTarget] = nil end end end end end function ExpEffectSystem.OnPlayBuffEffect(expEntity, effectPath, showChance) local show = false if showChance >= 1 or showChance == 0 then show = true else show = math.random() <= showChance end if not show then return nil end if LogicUpdateManager.IsInReconnectFastPlay() then return nil end local buffEffectPlayID = private._buffEffectPlayID + 1 private._buffEffectPlayID = buffEffectPlayID private._buffEffectPlayData[buffEffectPlayID] = effectPath local entityPlayData = private._buffEffectPlayData[expEntity] -- 若是玩家自己的武将,则只记录不播放(通过玩家显隐时执行ExpSystemManager.OnConvertRenderVisual让特效显隐) local playerSelfNotPlay = expEntity.Entity._logicInitData.IsPlayerSelf and (not expEntity.Entity.components.base.IsActive or not expEntity.Entity.components.base.IsFirstActiveDone) if entityPlayData == nil then local psPlayID = playerSelfNotPlay and 0 or (SystemManager.ShowPSEffect(expEntity.Entity, effectPath) or 0) entityPlayData = { PsPlayInfo = { [effectPath] = psPlayID }, PsCountInfo = { [effectPath] = 1 }, } private._buffEffectPlayData[expEntity] = entityPlayData else local psPlayID = entityPlayData.PsPlayInfo[effectPath] if psPlayID == nil then psPlayID = playerSelfNotPlay and 0 or (SystemManager.ShowPSEffect(expEntity.Entity, effectPath) or 0) entityPlayData.PsPlayInfo[effectPath] = psPlayID entityPlayData.PsCountInfo[effectPath] = 1 else entityPlayData.PsCountInfo[effectPath] = entityPlayData.PsCountInfo[effectPath] + 1 end end return buffEffectPlayID end function ExpEffectSystem.OnStopBuffEffect(expEntity, buffEffectPlayID, breakEffect) local effectPath = private._buffEffectPlayData[buffEffectPlayID] private._buffEffectPlayData[buffEffectPlayID] = nil local entityPlayData = private._buffEffectPlayData[expEntity] local count = entityPlayData.PsCountInfo[effectPath] count = count - 1 if count > 0 then entityPlayData.PsCountInfo[effectPath] = count else entityPlayData.PsCountInfo[effectPath] = nil local psPlayID = entityPlayData.PsPlayInfo[effectPath] if breakEffect then AuxUtil.BreakPsEffect(psPlayID) else AuxUtil.StopPsEffect(psPlayID) end entityPlayData.PsPlayInfo[effectPath] = nil end end function ExpEffectSystem.ReplayBuffEffect(expEntity) local entityPlayData = private._buffEffectPlayData[expEntity] if entityPlayData then local PsPlayInfo = entityPlayData.PsPlayInfo for effectPath in pairs(PsPlayInfo) do local newPlayID = SystemManager.ShowPSEffect(expEntity.Entity, effectPath) or 0 PsPlayInfo[effectPath] = newPlayID end end end function ExpEffectSystem.Unload(expEntity) local entityPlayData = private._buffEffectPlayData[expEntity] if entityPlayData then assert(next(entityPlayData.PsPlayInfo) == nil, "Assertion Failed: BuffEffect PlayID Not Clear When Entity Unload!") assert(next(entityPlayData.PsCountInfo) == nil, "Assertion Failed: BuffEffect Count Not Clear When Entity Unload!") private._buffEffectPlayData[expEntity] = nil end local hookEntityData = private._hookEntityData[expEntity] if hookEntityData then --assert(next(hookEntityData) == nil, "Assertion Failed: HookEntityData PlayID Not Clear When Entity Unload!") private._hookEntityData[expEntity] = nil end if private._repeatPlayLightEffectDic[expEntity] then private._repeatPlayLightEffectDic[expEntity] = {} end end function ExpEffectSystem.Clear() if next(private._buffEffectPlayData) then private._buffEffectPlayData = {} logging.error(LTAG.ERROR, "Assertion Failed: BuffEffect Not Clear When Level Exit!") end if next(private._hookData) then private._hookData = {} logging.error(LTAG.ERROR, "Assertion Failed: HookData Not Clear When Level Exit!") end if next(private._hookEntityData) then private._hookEntityData = {} logging.error(LTAG.ERROR, "Assertion Failed: HookEntityData Not Clear When Level Exit!") end end function ExpEffectSystem.OnFastPlayOver(entity) -- 查看所有的buff,播放其特效 for i,buffObject in ipairs(entity.components.buff.CurBuffObjectList) do local needPlayEffect = false local buffUpdateData = buffObject.BuffUpdateData local buffUpdateList = buffObject.BuffUpdateList if buffUpdateData and buffUpdateData.BuffLayer > 0 then needPlayEffect = true end if not needPlayEffect and buffUpdateList then for i, updateData in ipairs(buffUpdateList) do if updateData.BuffLayer > 0 then needPlayEffect = true break; end end end if needPlayEffect then buffObject:PlayEffect(buffObject:GetHost(), entity.components.replace:GetCurModelBuffResource(buffObject.BuffInfoData)) end end end function ExpEffectSystem.OnUpdate(deltaTime) for expEntity,info in pairs(private._waitingPlayEffectEntityDic) do local waitingCount = #info info.leftTime = info.leftTime - deltaTime if info.leftTime <= 0 then -- 到时了 if waitingCount > 0 then info.curPlayID = SystemManager.ShowPSEffect(expEntity.Entity, info[1]) table.remove(info , 1) info.leftTime = QueueEffectInterval else info.leftTime = 0 private._waitingPlayEffectEntityDic[expEntity] = nil -- 必须要等n秒后才能清空,如果立即清空,然后下一帧添加一个特效 ,那么这个后来特效会立刻播放。这不是策划想要的 。 end end end for expEntity,info in pairs(private._repeatPlayLightEffectDic) do for effectIdx, effectInfo in ipairs(info) do effectInfo.cdTimer = effectInfo.cdTimer - deltaTime if effectInfo.cdTimer <= 0 then expEntity:StopPsEffect(effectInfo.lightEffectData.EffectName)--手动停止,防止一些没有设置自毁的GO一直生成叠加 effectInfo.cdTimer = effectInfo.lightEffectData.Interval if effectInfo.lightEffectData.PlayPsType == LuaConst.LightEffectType.IdleLoopShow then if expEntity.Entity.components.aiBase.CurStateType == LuaConst.StateType.Idle and not expEntity.Entity.components.buff:CheckHasBuffState(LuaConst.SpecialBuffs.InRiding) then expEntity:PlayPsEffect(effectInfo.lightEffectData.EffectName) end elseif effectInfo.lightEffectData.PlayPsType == LuaConst.LightEffectType.LoopShow then expEntity:PlayPsEffect(effectInfo.lightEffectData.EffectName) else expEntity:PlayPsEffect(effectInfo.lightEffectData.EffectName) end end end end end function ExpEffectSystem.AddQueueEffect(expEntity ,effectPath) local info = private._waitingPlayEffectEntityDic[expEntity] if info == nil then private._waitingPlayEffectEntityDic[expEntity] = {effectPath, leftTime = 0 } else table.insert(info, effectPath) end end function ExpEffectSystem.OnDead(expEntity) if private._waitingPlayEffectEntityDic[expEntity] then private._waitingPlayEffectEntityDic[expEntity] = nil end end function ExpEffectSystem.OnSwitchHero(fromExpEntity, toExpEntity, dontBackground) if not dontBackground then fromExpEntity:BreakPsEffectAll() local info = private._waitingPlayEffectEntityDic[fromExpEntity] if info then if info.curPlayID then -- 防止还没播放完的特效再次显示时播放了剩余时长 AuxUtil.StopPsEffect(info.curPlayID) end private._waitingPlayEffectEntityDic[fromExpEntity] = nil end toExpEntity:ReplayAllBuffEffect() toExpEntity:RecoverActorPsEffect() end end function ExpEffectSystem.BeforeChangeRideState(expEntity) local curLightEffectIDs = expEntity.components.model.lightEffectIDs if curLightEffectIDs then for _,lightEffectId in ipairs(curLightEffectIDs) do local lightEffectData = DataSheetManager.LightEffect:GetDataByID(lightEffectId) local effectStr = lightEffectData.EffectName if not LevelManager.IsNoBattleLevel() and BattleLightEffectPathDic[lightEffectId] ~= nil then effectStr = BattleLightEffectPathDic[lightEffectId] end if lightEffectData.PlayPsType == LuaConst.LightEffectType.Normal then expEntity:StopPsEffect(effectStr) end end end end function ExpEffectSystem.AfterChangeRideState(expEntity) local curLightEffectIDs = expEntity.components.model.lightEffectIDs if curLightEffectIDs then for _,lightEffectId in ipairs(curLightEffectIDs) do local lightEffectData = DataSheetManager.LightEffect:GetDataByID(lightEffectId) local effectStr = lightEffectData.EffectName if not LevelManager.IsNoBattleLevel() and BattleLightEffectPathDic[lightEffectId] ~= nil then effectStr = BattleLightEffectPathDic[lightEffectId] end if lightEffectData.PlayPsType == LuaConst.LightEffectType.Normal then expEntity:PlayPsEffect(effectStr) end end end end function ExpEffectSystem.ShowOrHideLightEffect(expEntity, lightEffectID, isShow) if lightEffectID == nil or lightEffectID <= 0 then return end local lightEffectData = DataSheetManager.LightEffect:GetDataByID(lightEffectID) if not lightEffectData then logging.error(LTAG.ERROR, "[策划] :光效表没有发现数据 :id=", lightEffectID) return end if CTL.HotFix.CanAssetLoad(string.format(EFFECTPATH, lightEffectData.EffectName)) == false then return end local effectStr = lightEffectData.EffectName if not LevelManager.IsNoBattleLevel() and BattleLightEffectPathDic[lightEffectID] ~= nil then effectStr = BattleLightEffectPathDic[lightEffectID] end if isShow then table.insert(expEntity.components.model.lightEffectIDs,lightEffectID) if lightEffectData.PlayPsType == LuaConst.LightEffectType.Normal then SystemManager.ShowPSEffect(expEntity.Entity, effectStr, true) elseif lightEffectData.PlayPsType == LuaConst.LightEffectType.Footprint then effectStr = UTL.String.GetIdByString(effectStr) -- 这里做处理是为了在玩家更换脚底特效时间隔能直接根据速度调整 local isFollow = TownCartManager.IsFollowingNPC(expEntity.Entity) if isFollow then local acid = SyncMoveManager.GetAcIdOfEntity(expEntity.Entity) local followSpd = TownCartManager.FollowMoveInfo[acid].speed *LuaConst.LogicFrameRate local interval = expEntity:GetFootInterval(isFollow, followSpd, lightEffectData.Interval) expEntity.RenderEntity:StarFootprintEffect(effectStr, interval) else expEntity.RenderEntity:StarFootprintEffect(effectStr, lightEffectData.Interval) end end if lightEffectData.PlayPsType == LuaConst.LightEffectType.IdleLoopShow or lightEffectData.PlayPsType == LuaConst.LightEffectType.LoopShow then local curRepeatList = private._repeatPlayLightEffectDic[expEntity] if curRepeatList == nil then curRepeatList = {} end local tmpLightEffectData = {lightEffectData = lightEffectData, cdTimer = lightEffectData.Interval} table.insert(curRepeatList, tmpLightEffectData) private._repeatPlayLightEffectDic[expEntity] = curRepeatList end else if lightEffectData.PlayPsType == LuaConst.LightEffectType.Normal then expEntity:StopPsEffect(effectStr) expEntity:RemoveActorPsEffectRecord(effectStr) elseif lightEffectData.PlayPsType == LuaConst.LightEffectType.Footprint then expEntity.RenderEntity:StopFootprintEffect() end table.removebyvalue(expEntity.components.model.lightEffectIDs, lightEffectID) if lightEffectData.PlayPsType == LuaConst.LightEffectType.IdleLoopShow or lightEffectData.PlayPsType == LuaConst.LightEffectType.LoopShow then --因为此时已经会出现一次性播放多个特效的情况,所以此时就不能直接暴力地将表置空或赋值为nil了,否则可能会影响到其他正常播放的Loop效果 for effectIdx, info in ipairs(private._repeatPlayLightEffectDic[expEntity]) do if info.lightEffectData.ID == lightEffectID then table.remove(private._repeatPlayLightEffectDic[expEntity], effectIdx) end end end end end return ExpEffectSystem