local Missile = require "Battle/Logic/Missile/Missile" local ExpMissile = require "Battle/Expression/ExpMissile/ExpMissile" local TriggerMissileSystem = require "Battle/Logic/Missile/MissileSystem/TriggerMissileSystem" local RetinueSystem = require"Battle/Logic/Entity/System/RetinueSystem" local MissileManager = {} local private = { --call出的随机延迟执行列表 _toDoCallList = {}, --每个子弹都有一个唯一ID _tempMissileID = 0, -- 每个表现子弹的唯一ID _tempExpMissileID = 0, --所有激活的子弹 _activeMissileList = {}, -- 本帧内要回收的子弹 _removeList = {}, --延迟添加的回赠buff (不用定时器 ,不用battleEffect ,否则破坏整体结果 ;不放在buffsystem中,是因为导致混乱,很容易被误用,后期很容易出bug) _feedBackDelayBuffList = {}, } local fireParam = {} local table_insert=table.insert local table_removebyvalue=table.removebyvalue local table_remove=table.remove local ipairs=ipairs local FP_Zero = FP.Const.Zero local CtrlMissileTypeByJoystick_MissileEnd = LuaConst.CtrlMissileTypeByJoystick.MissileEnd local _tempV1 = FP.newV1(0) --[[ 生成子弹 --]] function MissileManager.CreateMissile(entity,callData,callBulletInfo) private._tempMissileID = private._tempMissileID + 1 local missile = Missile:new() missile.MissileId = private._tempMissileID missile:Init(entity,callData,callBulletInfo) return missile end function MissileManager.CreateExpMissile(missile) private._tempExpMissileID = private._tempExpMissileID + 1 local expMissile = ExpMissile:new(missile) expMissile.ExpMissileId = private._tempExpMissileID return expMissile end -- 延迟的buff ;这些buff的来源时自己 delayTime 毫秒 function MissileManager.AddDelayAddBuff( delayTime,attacker,buffID,ctrlParam) table_insert(private._feedBackDelayBuffList, {delayTime = FP.newV1(delayTime),attacker = attacker,buffID = buffID,ctrlParam = ctrlParam}) end --创建并发射钩子子弹 function MissileManager.CreateHookMissileAndFire(entity, hookEntity, callData, callBulletInfo, hookStartEntity) local missile = MissileManager.CreateMissile(entity,callData,callBulletInfo) local fireParam = { Attacker = entity, Forward = entity.components.move:GetLogicRotateDir():cloneV2(), Pos = entity:GetLogicPos():cloneV2(), PosY = FP.newV1(0), PosOffset = FP.newV2(0,0), FireRotate = FP.newV2(0,0), OffsetY = FP.newV1(0), IsTracker = missile.IsTracker, EffForward = FP.newV1(0), } LuaShoveManager.GetHeightByPos(hookEntity:GetLogicPos() ,fireParam.PosY) missile:EstablishHook(hookEntity, hookStartEntity or entity) MissileManager.FireMissile(missile,fireParam) TriggerMissileSystem.TryTriggerHookMissile(missile) end function MissileManager.GetHookCount(entity) local count = 0 for i,missile in ipairs(private._activeMissileList) do if missile.hookEntity == entity or missile.hookStartEntity == entity then count = count + 1 end end return count end function MissileManager.GetHookCountOfMissileID(entity, missileID) local count = 0 for i,missile in ipairs(private._activeMissileList) do if missile.MissileData.ID == missileID and missile.hookEntity == entity or missile.hookStartEntity == entity then count = count + 1 end end return count end function MissileManager.HasHookBetween(hookEntityA, hookEntityB) for i,missile in ipairs(private._activeMissileList) do if missile.hookEntity == hookEntityA and missile.hookStartEntity == hookEntityB or missile.hookEntity == hookEntityB and missile.hookStartEntity == hookEntityA then return true end end end function MissileManager.HasHookBetweenAToB(startA, endB) -- 严格要求是 start是A ,end是B for i,missile in ipairs(private._activeMissileList) do if missile.hookStartEntity == startA and missile.hookEntity == endB then return true end end end --删除指定MissileID的、钩上自己(不是自己发射的)的钩子子弹 function MissileManager.BreakHookMissileByIDAsHookEntity(missileID, hookEntity) local missile for i=#private._activeMissileList, 1, -1 do missile = private._activeMissileList[i] if missile.MissileData.ID == missileID then if missile.hookEntity == hookEntity or missile.hookStartEntity == hookEntity then missile:BreakHook() end end end end --删除指定MissileID的、钩上自己 和 指定entity的钩子子弹 function MissileManager.BreakHookMissileByIDAndEntity(missileID, hookStartEntity, hookEntity) local missile for i=#private._activeMissileList, 1, -1 do missile = private._activeMissileList[i] if missile.MissileData.ID == missileID then if missile.hookEntity == hookEntity and missile.hookStartEntity == hookStartEntity then missile:BreakHook() end end end end --删除指定MissileID的、attacker发射的钩子子弹 function MissileManager.BreakHookMissileByIDAsAttacker(missileID, attacker) local missile for i=#private._activeMissileList, 1, -1 do missile = private._activeMissileList[i] if missile.MissileData.ID == missileID then if missile.Attacker == attacker then --这块确实是需要访问missile.Attacker,而不是missile.startHookEntity missile:BreakHook() end end end end function MissileManager.BreakHookOf2Entities(hookEntityA, hookEntityB) local missile for i=#private._activeMissileList, 1, -1 do missile = private._activeMissileList[i] if (missile.hookEntity == hookEntityA and missile.hookStartEntity == hookEntityB) or (missile.hookEntity == hookEntityB and missile.hookStartEntity == hookEntityA) then missile:BreakHook() end end end function MissileManager.CancelMissileOfAttackerByMissileID(missileID, attacker) local missile for i=#private._activeMissileList, 1, -1 do missile = private._activeMissileList[i] if missile.MissileData.ID == missileID then if missile.Attacker == attacker then --这块确实是需要访问missile.Attacker,而不是missile.startHookEntity missile:Cancelled() end end end end function MissileManager.CancelMissileOfAttacker(attacker) local missile for i=#private._activeMissileList, 1, -1 do missile = private._activeMissileList[i] if missile.Attacker == attacker then --这块确实是需要访问missile.Attacker,而不是missile.startHookEntity missile:Cancelled() end end end function MissileManager.CancelParentDeadMissileOfAttacker(attacker) local missile for i=#private._activeMissileList, 1, -1 do missile = private._activeMissileList[i] if missile.Attacker == attacker and missile.ParentDead then --这块确实是需要访问missile.Attacker,而不是missile.startHookEntity missile:Cancelled() end end end --[[ 发射子弹 --]] function MissileManager.FireMissile(missile, fireParam) table_insert(private._activeMissileList,missile) missile:Fire(fireParam) BattleEventSystem.OnEntityMissileStart(missile) end --[[ 回收子弹 --]] function MissileManager.CollectMissile(missile) table_insert(private._removeList,missile)-- GWS:不能在这移除,否则update中的_activeMissileList乱序(第一个移除,直接执行第三个 ,第二个跳过了) end --[[ 逻辑的Tick --]] local function OnLogicUpdateNotNil() private.TryBornMissileOrMonster_Update() for key,missile in ipairs(private._activeMissileList) do if not missile.Unload then missile:OnLogicUpdate() end end local removeCount = #private._removeList for i = removeCount,1 , -1 do local removeMissile = private._removeList[i] table_removebyvalue(private._activeMissileList,removeMissile,true) table_remove(private._removeList) end --延迟加的feedback buff local count = #private._feedBackDelayBuffList for i = count, 1 , -1 do local info = private._feedBackDelayBuffList[i] if info.attacker and info.attacker:IsActive() then -- 只管是否激活 ,不用管死亡 ,死亡由策划的死亡互斥负责 local timeScale = info.attacker.components.timeScale:GetCurTimeScale() _tempV1:Reset(LuaConst.TimeSpanMs):Mul(timeScale) info.delayTime:Sub(_tempV1) if info.delayTime <= FP_Zero then SystemManager.AddBuff(info.attacker,info.buffID,info.attacker,nil,info.ctrlParam) table.remove(private._feedBackDelayBuffList,i) end else table.remove(private._feedBackDelayBuffList,i) -- 人都没了 ,就不再留着反哺buff end end end MissileManager.OnLogicUpdate = OnLogicUpdateNotNil --region OnLogicUpdate的重定向 local function OnLogicUpdateNil() end function MissileManager.SetLogicUpdateNil() MissileManager.OnLogicUpdate = OnLogicUpdateNil end function MissileManager.SetLogicUpdateNotNil() MissileManager.OnLogicUpdate = OnLogicUpdateNotNil end function MissileManager.LogicUpdateIsNil() return MissileManager.OnLogicUpdate == OnLogicUpdateNil end --endregion OnLogicUpdate的重定向 --[[ 表现的Tick --]] function MissileManager.OnExpUpdate(deltaTime) for key,missile in ipairs(private._activeMissileList) do missile:OnExpUpdate(deltaTime) end end --[[ 切换场景等 清空子弹 ]]-- function MissileManager.Clear() for i, missile in ipairs(private._activeMissileList) do missile:Cancelled(true) end private._toDoCallList = {} private._activeMissileList = {} private._tempMissileID = 0 private._tempExpMissileID = 0 private._feedBackDelayBuffList = {} end function MissileManager.AddCallAction(entity, calldata, forward, position, firePosY, posOffset, targetPos, offsetY, fireRotate, callBulletInfo, delayTime ) local callAction = {entity = entity, calldata = calldata, forward = forward, position = position, firePosY = firePosY, posOffset = posOffset, targetPos = targetPos, offsetY = offsetY, fireRotate = fireRotate, callBulletInfo = callBulletInfo ,delayTime = delayTime} table.insert(private._toDoCallList,callAction) end local _tempDeltaTime = FP.newV1(0) local _tempToRemoveList = {} function private.TryBornMissileOrMonster_Update() local count = #private._toDoCallList if count <= 0 then return end for i,callAction in ipairs(private._toDoCallList) do local entity = callAction.entity _tempDeltaTime:Reset(entity.components.timeScale:GetCurTimeScale()) _tempDeltaTime:Mul(LuaConst.TimeSpanMs) callAction.delayTime:Sub(_tempDeltaTime) if callAction.delayTime < FP_Zero then -- 这样写包含了,第0毫秒的时间 table.insert(_tempToRemoveList, i) MissileManager.DoCallAction(entity, callAction.calldata, callAction.forward, callAction.position, callAction.firePosY, callAction.posOffset, callAction.targetPos, callAction.offsetY, callAction.fireRotate, callAction.callBulletInfo) end end for i=#_tempToRemoveList,1,-1 do table_remove(private._toDoCallList, _tempToRemoveList[i]) table_remove(_tempToRemoveList) end end function MissileManager.DoCallAction(entity, data, forward, position, firePosY, posOffset, targetPos, offsetY, fireRotate, callBulletInfo) if data.BulletID > 0 or data.TrackerID > 0 then local missile = MissileManager.CreateMissile(entity,data,callBulletInfo) fireParam.Forward = forward -- 子弹的自身朝向 和 飞行方向 fireParam.Pos = position fireParam.PosY = firePosY fireParam.PosOffset = posOffset -- 子弹的相对偏移 fireParam.Attacker = entity fireParam.ParabolaTargetPos = targetPos fireParam.OffsetY = offsetY -- 子弹高度偏移 fireParam.IsTracker = missile.IsTracker fireParam.FireRotate = fireRotate -- 判断追踪子弹时候向回飞,并给出表现轨迹 fireParam.EffForward = callBulletInfo.EffForward if fireParam.IsTracker and callBulletInfo.ctrlTypeByJoystick == CtrlMissileTypeByJoystick_MissileEnd then fireParam.TrackerEndPos = callBulletInfo.skillJoyStickPos:cloneV2() -- 防止后续的技能摇杆干扰之前的子弹;以后策划要实时遥控导弹,则需要去掉cloneV2() else fireParam.TrackerEndPos = nil end entity.components.drive:TrySetRandomAngle(fireParam) if missile.MoveType == LuaConst.MissileMoveType.Parabola and targetPos == nil then logging.error(LTAG.ERROR, "[战斗策划] :抛物线子弹没有发现目标点(AI配置点或者敌人目标点),AI没配置固定点或者技能释放时没有找到target") missile:Cancelled(true) -- 需要强制销毁子弹,否则会一直存在missilemanager中 else MissileManager.FireMissile(missile, fireParam) end elseif data.MonsterID > 0 then --召唤的是怪 --尝试看看这个点能不能寻路到父亲,寻不到那就是找和父亲的最近挤压位置 (无视阻挡的怪始终认为能寻路到) if (not data.NotShove) and private.CanNotFindPath(entity, position ,entity:GetLogicPos(), true) then -- 这个寻路有限制 ,强厚度必须大于2米,否则会认为能寻路到 LuaShoveManager.GetFurthestPos(entity:GetLogicPos(),position ,position) end if entity then local isSuccess,retinueData = DataSheetManager.RetinueData:TryGetDataByID(data.MonsterID) if isSuccess and retinueData.UseDevelopOfHeroID then if not LevelManager.IsShowRetinue() then -- 关卡限制不能显示随从 return end local bornAreaRadius = FP.newV1(retinueData.BornAreaRadius) if FP.V2.IsDistanceSqrGreater(position, entity:GetLogicPos(), bornAreaRadius * bornAreaRadius) then -- 离主人太远了,重新选个出生点 position:Reset(RetinueSystem.GetRandomPosInBornAreaByData(entity, retinueData)) end EntityFactory.SetParams("Name", entity._logicInitData.Name) local armyData = LevelPlayerManager.GetPlayerArmyData(entity:GetPlayerID(), entity) local extraHeroInfo = LevelPlayerManager.GetExtraHeroInfo(armyData, retinueData, entity:GetPlayerID(), entity._logicInitData.Acid) if extraHeroInfo then EntityFactory.SetEffectBreak_Weapon(extraHeroInfo.mainHeroBreakNums, extraHeroInfo.isMagicWeaponActive, extraHeroInfo.mainHeroWeaponLevels) --↓ 策划去掉了继承皮肤的需求,但是未来可能还需要,先注掉 [gws:2023-3-14 17:46 策划说 : 貂蝉一定紫皮 ,但是神兵要用皮肤] --EntityFactory.SetHeroAppearance_ID(extraHeroInfo.heroAppearanceId) EntityFactory.SetWeaponAppearance_ID(extraHeroInfo.weaponAppearanceId) --↑ EntityFactory.SetDyeAppearanceList(extraHeroInfo.mainDyeCurPartList) -- else -- logging.error(LTAG.ERROR, "HYW : extraHeroInfo nil Error @MissileManager.DoCallAction ") end end end EntityFactory.SetPos_Dir(position, forward) EntityFactory.SetType_ID(LuaConst.CharacterType.Monster, data.MonsterID) if data.IsInheritCampID then EntityFactory.SetParams("CampID", entity:GetCampID()) end EntityFactory.SetParams("SkipFindFinalPos", data.NotShove) EntityFactory.SetParams("MasterEntity", entity) local replaceModelId = entity.components.replace:GetCurModelCallMonsterModelID(callBulletInfo.SkillID,data.MonsterID) if replaceModelId then EntityFactory.SetParams("ExpModelID",replaceModelId) end local calledEntity = EntityFactory.DoCreateEntity() if callBulletInfo.ctrlTypeByJoystick == CtrlMissileTypeByJoystick_MissileEnd then -- 设置怪物的强制行走点 calledEntity.components.aiBase:SetCalledSkillJoystickEndPos(callBulletInfo.skillJoyStickPos:cloneV2()) end if data.NotShove then -- 有可能在非行走面,需要设置下高度 private.TrySetMonsterY(calledEntity,entity) end if data.SonMonsterType ~= 0 then SystemManager.OnAdopted(calledEntity, entity , data.SonMonsterType == 1 ,data.HpInherit, data.InheritAttrType) --0不继承, 1 表示独立个体,不再受父影响 ,2 实时更新 ; end if data.ParentDead then --ParentDead儿子给父亲殉葬 SystemManager.BindDead(calledEntity, entity ) end end end local temp_pathResult = {} --ignorePriorityWall: 是否忽略优先级墙 function private.CanNotFindPath(entity, startPos ,endPos, ignorePriorityWall) local startX,startZ = LuaShoveManager.ConvertPosToGrid(startPos) local endX,endZ = LuaShoveManager.ConvertPosToGrid(endPos) if startX == endX and startZ == endZ then -- 由于寻路不把起点返回 ,所以在同一个格子认为能寻路成功 return false end entity.components.path.CurFindPathCount = entity.components.path.CurFindPathCount + 1 local count = BattleExt.GridFindPath(startX, startZ, endX, endZ, entity.EntityID, entity.components.path.CurFindPathCount, false, false, ignorePriorityWall, temp_pathResult) table.clear(temp_pathResult) return count == 0 end function private.TrySetMonsterY(entity,masterEntity) local posY = entity:GetLogicPosY() if FP.tonumberV1(posY) == -1000 then -- 非行走面内 SystemManager.SetCallEntityY(entity ,masterEntity:GetLogicPosY()) end end -- 宠物发射子弹 function MissileManager.DoCallPetMissile(entity, missileID, forward, position, targetEntity, offsetY) local data = {TrackerID = 0, BulletID = missileID} local callBulletInfo = {TrackerTarget = targetEntity, MissileAimPos = targetEntity:GetLogicPos():cloneV2()} local missile = MissileManager.CreateMissile(entity,data,callBulletInfo) fireParam.Forward = forward -- 子弹的自身朝向 和 飞行方向 fireParam.Pos = position fireParam.PosY = entity:GetLogicPosY() fireParam.PosOffset = nil -- 子弹的相对偏移 fireParam.Attacker = entity fireParam.ParabolaTargetPos = nil fireParam.OffsetY = FP.newV1(offsetY) -- 子弹高度偏移 fireParam.IsTracker = false fireParam.FireRotate = nil fireParam.EffForward = nil fireParam.TrackerEndPos = nil MissileManager.FireMissile(missile, fireParam) end -- 对某些子弹进行特殊处理 function MissileManager.AddAngleSpeed(missileID,val) for i, missile in ipairs(private._activeMissileList) do if missile.MissileData.ID == missileID then missile:OverrideAngleSpeedAdd(val) end end end return MissileManager