local Missile = class("Missile") local private = { ConstCloseDis = 0.5, hookMissileCount = 0,--用来调试,稳定之后可以删除 } --加载需要的模块 local CircleMissileShape = require "Battle/Logic/Missile/MissileShape/CircleMissileShape" local BoxMissileShape = require "Battle/Logic/Missile/MissileShape/BoxMissileShape" local SectorMissileShape = require "Battle/Logic/Missile/MissileShape/SectorMissileShape" local BattleEventSystem = BattleEventSystem local SystemManager = SystemManager local tonumberV2= FP.tonumberV2 local tonumberV1=FP.tonumberV1 local newV1=FP.newV1 local newV2=FP.newV2 local Const_Zero=FP.Const.Zero local MissileShapeType=LuaConst.MissileShapeType local Const_NegOne=FP.Const.NegOne local Const_One=FP.Const.One local LuaConst_TimeSpan=LuaConst.TimeSpan local LuaConst_TimeSpanMs=LuaConst.TimeSpanMs local IsDistanceGreater=FP.V2.IsDistanceGreater local Less=FP.Less local Vector3_New=Vector3.New local LuaConst_FireNextType_End=LuaConst.FireNextType.End local LuaConst_FireNextType_WaitCancle=LuaConst.FireNextType.WaitCancle local LuaConst_FireNextType_CancelAndNoHit = LuaConst.FireNextType.CancelAndNoHit local LuaConst_FireNextType_CancelAndHitted = LuaConst.FireNextType.CancelAndHitted local LuaConst_FireNextType_AfterBornTime = LuaConst.FireNextType.AfterBornTime local LuaConst_FireNextType_CancelAndLastHitFriend = LuaConst.FireNextType.CancelAndLastHitFriend local LuaConst_FireNextType_CancelAndLastHitEnemey = LuaConst.FireNextType.CancelAndLastHitEnemey local Normalize= FP.V2.Normalize local LuaConst_SpecialBuffs_InSuperWeak=LuaConst.SpecialBuffs.InSuperWeak --OnMissileWaitCancel 的事件只触发一次 local _isWaitCancel = false local ipairs=ipairs --[[ 初始化 注意:callBulletInfo可能为nil! --]] function Missile:Init(entity,callData,callBulletInfo) local isTracker = callData.TrackerID > 0 local dataID = isTracker and callData.TrackerID or callData.BulletID --子弹的配置 if isTracker then --追踪弹 self.IsTracker = true self.MissileData = {} local data = entity:GetTrackerData(dataID) if data then for k in DataSheetManager.TrackerData:GetPropertyEnumerator() do self.MissileData[k] = data[k] end end self.MissileData.DelayMoveFrame = 0 self.MissileData.HideDelayFrameIsShow = false --self.TrackerTarget 目前的含义已经变为子弹目标,实际命名应该为 MissileTarget。为了减轻测试压力,依然保持原来的命名不再重构。 self.TrackerTarget = nil --该变量在为跟踪弹时,是跟踪弹的目标。不为跟踪弹时,依然也有意义,是子弹的目标。这个变量在一开始设计为仅给跟踪弹用,后来被设计为所有子弹都可以用,含义拓展为了子弹目标(2022年12月27日)。 self.TrackerVirtualPos = nil self.InFreeFly = self.MissileData.FreeFlySaveTime > Const_Zero --是否处于自由飞行阶段 self.FreeFlyTime = newV1(0) self.TrackTime = newV1(0) if self.MissileData.AimNodeSet then local aimTable = self.MissileData.AimNodeSet local x = aimTable[1] local z = aimTable[3] self.TrackerOffset = newV2(x, z) else self.TrackerOffset = newV2(0, 0) end else self.IsTracker = false self.MissileData = entity:GetMissileData(dataID) --多段伤害子弹,上次触发时间 self.LastHitTime = -self.MissileData.DamageIntervalFrame[1] end if callBulletInfo then self:SetTrackerTarget(callBulletInfo.TrackerTarget) self.bounceInfo = callBulletInfo.bounceInfo end self.AfterBornTimeNextMissileList = nil -- 子弹实时检测发射下一个子弹的列表(发射下一个子弹类型为5的列表) for i,nextInfo in ipairs(self.MissileData.FireMissileData_Table) do if nextInfo.Type == LuaConst_FireNextType_AfterBornTime then if self.AfterBornTimeNextMissileList == nil then self.AfterBornTimeNextMissileList = {} end table.insert(self.AfterBornTimeNextMissileList ,nextInfo) end end if callBulletInfo == nil then--钩子子弹 local skillComp = entity.components.skillBase self.SkillID = (skillComp.CurSkillData and skillComp.CurSkillData.ID) or (skillComp.LastSkillData and skillComp.LastSkillData.ID) else self.SkillID = callBulletInfo.SkillID self.MissileAimPos = callBulletInfo.MissileAimPos:cloneV2() end self.hitResource = entity.components.replace:GetCurModelHitResource(self.SkillID,self.MissileData.HitResource) --↓↓↓钩子特性支持↓↓↓↓ self.hookEntity = nil --是否钩住目标了(仅钩子类子弹使用) --↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ self.overrideShapeParam1 = nil self.overrideShapeParam2 = nil self.overrideShapeParam3 = nil self.overrideAngleSpeed = nil --子弹存在总时长(单位毫秒) self.TotalTime = newV1(0) --子弹由谁发射 self.Attacker = nil --用哪个call调用出来的 self.CallData = callData self.ParentDead = self.CallData.ParentDead --子弹是否忽略发射者 self.IgnoreAttacker = nil --子弹要检查触碰的人 --不要改变这个列表的内部元素,因为为了节省性能这个列表会引用到公共的EntityList --为了节省性能,这是一个二维数组 self.TriggerList = nil --子弹发射位置 self.InitPos = newV2(0, 0) --子弹发射位置Y self.InitPosY = newV1(0) --子弹位置偏移 self.PosOffset = newV2(0, 0) --子弹位置偏移Y self.OffsetY = newV1(0) --子弹的位置偏移距离(单位 米) self.PosOffsetDistance = newV1(0) self.FireRotate = newV2(0, 1) -- 子弹自身朝向 self.FireDir = newV2(0, 1) -- 子弹出生的移动朝向(没有被各种改变的原始值) InitMoveDir --追踪子弹是否跟随角色(只给追踪弹的自由飞阶段使用) self.IsBinding = self.MissileData.IsBinding --子弹Y的速度 (初始为配表的速度) self.SpeedY = self.MissileData.MoveSpeedY and self.MissileData.MoveSpeedY:cloneV1() or Const_Zero -- 子弹的移动类型 self.MoveType = self.MissileData.MoveType --子弹是否暂停 self.Pause = false self.lastHitEntity = nil --最后击中的单位 --钩子Check↓↓↓↓ if self:IsHook() then if self.MissileData.ShapeType ~= MissileShapeType.Box then logging.error(LTAG.ERROR, "HYW :子弹碰撞类型错误!钩子子弹只能用矩形碰撞。错误子弹ID : " .. tostring(self.MissileData.ID)) end end --Check↑↑↑↑ if self.MissileData.ShapeType == MissileShapeType.Circle then self.CurMissileShape = CircleMissileShape elseif self.MissileData.ShapeType == MissileShapeType.Box then self.CurMissileShape = BoxMissileShape elseif self.MissileData.ShapeType == MissileShapeType.Sector then self.CurMissileShape = SectorMissileShape end ------------------------------------------------- --当前时间(单位:ms) 备注:1. 策划需求:子弹的伤害生效时间为 [start,end) ,想要无伤害配置[-1,0) self.CurTime = newV1(0) --上一帧时间 self.LastTime = newV1(0) --当前逻辑位置 self.Pos = newV2(0,0) --当前逻辑高度 self.PosY = newV1(0) --当前移动方向/朝向 self.MoveDir = newV2(0,1) --子弹命中的总人次数,命中一定次数后删除(注意是人次,不是人数) self.TotalHitEntityCount = 0 --一次伤害生效的时候,已经伤害过的人列表 key=eid ,value = hitTimestamp self.HitEntityIdList = {} self.CurFrameHitEntityCount = 0 -- 当前帧碰撞 entity 的 数量 self.CurFrameHitEntityIdList = {} -- 当前帧碰撞 entity 的 id 列表 --对应的表现子弹 local subRenderEntityPosList = entity.ExpEntity:GetSubRenderEntityOffsetList() if subRenderEntityPosList then -- 如果是集体模型,每个模型都要有个子弹效果 self.ExpMissileList = {} local count = #subRenderEntityPosList / 2 for i = 1,count do local expmissile = MissileManager.CreateExpMissile(self) table.insert(self.ExpMissileList , expmissile) end else self.ExpMissile = MissileManager.CreateExpMissile(self) end self.AttackRange = nil --这玩意是一个Transform,不是AttackRange的实例。这个代码不规范。 self:TryCreateAttackRange(entity) -- 工具代码:显示范围 if callBulletInfo then if callBulletInfo.DynamicSkillDmgFactor then self:SetDynamicDamageFactor(callBulletInfo.DynamicSkillDmgFactor) end end end -- 工具代码:显示范围 function Missile:TryCreateAttackRange(entity) if self.AttackRange then AttackRange.DisposeBullet(self.MissileId) self.AttackRange = nil end if entity and GlobalBattleData.GetIfEntityShowAttackRange(entity._logicInitData.EntityID) then local data=self.MissileData self.AttackRange = true if data.ShapeType == MissileShapeType.Box then local x=tonumberV1(data.OffsetX) local z=tonumberV1(data.OffsetZ) local width= tonumberV1(self.overrideShapeParam1 or data.ShapeParam1) local height= tonumberV1(self.overrideShapeParam2 or data.ShapeParam2) AttackRange.CreateBox(self.MissileId,x,z,width,height) elseif data.ShapeType==MissileShapeType.Circle then --new↓ 支持内径(圆环形状) local x=tonumberV1(data.OffsetX) local z=tonumberV1(data.OffsetZ) local radius= tonumberV1(self.overrideShapeParam1 or data.ShapeParam1) local innerRadius= tonumberV1(self.overrideShapeParam3 or data.ShapeParam3) AttackRange.CreateSector(self.MissileId,x,z,radius,360,innerRadius) --new↑ elseif data.ShapeType==MissileShapeType.Sector then local x=tonumberV1(data.OffsetX) local z=tonumberV1(data.OffsetZ) local radius= tonumberV1(self.overrideShapeParam1 or data.ShapeParam1) local angle= tonumberV1(self.overrideShapeParam2 or data.ShapeParam2) --这个是扇形一半的角度 local innerRadius= tonumberV1(self.overrideShapeParam3 or data.ShapeParam3) AttackRange.CreateSector(self.MissileId,x,z,radius,angle * 2,innerRadius) end end end --[[ 发射 --]] function Missile:Fire(fireParam) self.IsFired = true self.Attacker = fireParam.Attacker self.AttackerAttr = self.Attacker.components.attr self.AttackerBase = self.Attacker.components.base self.AttackerTimescale = self.Attacker.components.timeScale self.AttackerBuff = self.Attacker.components.buff self.TotalHitEntityCount = 0 self.LastTime:Reset(Const_NegOne) self.CurTime:Reset() self.PosOffset:Reset(fireParam.PosOffset) self.FireRotate:Reset(fireParam.FireRotate) self.EffForward = fireParam.EffForward if self.IsTracker then self.TrackerForcePos = fireParam.TrackerEndPos if self.FireRotate and (self.FireRotate.x > newV1(90) or self.FireRotate.x < newV1(-90)) then self.TrackAccelerate = self.MissileData.TrackFlySpd / Const_One self.TrackSpeed = -self.MissileData.TrackFlySpd self.NeedTrackAccelerate = true else self.TrackAccelerate = nil self.TrackSpeed = self.MissileData.TrackFlySpd self.NeedTrackAccelerate = false end end local driveData = self.Attacker.components.drive:GetDriveData() if driveData and driveData.FireMissileNeedY then -- gws:暂时只给被驾驶的载具使用 ,如果其他也要,就该这个判断到monster表比较合适了 self.SpeedY = self.Attacker.components.drive:GetSpeedY(self.MissileData.MoveSpeed) end SystemManager.OnMissileFire(self, fireParam) end function Missile:SetTrackerTarget(target) self.TrackerTarget = target end --参数是FP function Missile:SetDynamicDamageFactor(dmgFactor) self.DynamicDamageFactor = dmgFactor end --返回值是FP function Missile:GetDynamicDamageFactor() return self.DynamicDamageFactor end --返回值是FP function Missile:GetSkillID() return self.SkillID end --[[ 逻辑的Tick --]] local _tempfixp1 = newV1(0) local _tempV2_a = newV2(0, 0) local _tempV2_b = newV2(0, 0) function Missile:OnLogicUpdate() if not self.AttackerAttr:IsAlive() or not self.AttackerBase.IsActive then --策划需求:人死亡后,子弹也要消失(也可以放在entity的OnDead遍历所有子弹,但是子弹会比兵少,性能上会好些) if self.ParentDead then self:Cancelled(true) return end end -- 看下是否到了发射下个子弹的时间点了 if self.AfterBornTimeNextMissileList then for i,nextInfo in ipairs(self.AfterBornTimeNextMissileList) do local fireTime = nextInfo.Param if self.LastTime < fireTime and self.CurTime >= fireTime then SystemManager.CallNextBullet(self, nextInfo, LuaConst_FireNextType_AfterBornTime) end end end local timeScale = self.AttackerTimescale.CurMissileTimeScale if self.hookEntity ~= nil then -- self:IsHooked() local shouldBreak,isTooFarBreak = self:ShouldBreakHook() if shouldBreak then self:BreakHook(isTooFarBreak) else SystemManager.OnMissileUpdate(self) BattleEventSystem.AfterHookMissileUpdate(self) end elseif self.IsTracker then if self.InFreeFly then --如果人被打虚弱了,子弹消失 if self.AttackerBuff:GetBuffUpdateData(LuaConst_SpecialBuffs_InSuperWeak) then self:Cancelled(true) return end SystemManager.OnMissileUpdate(self) self.FreeFlyTime:Add( _tempfixp1:Reset(LuaConst_TimeSpanMs):Mul(timeScale)) --print("freeFly", tostring(self.FreeFlyTime)) if self.FreeFlyTime >= self.MissileData.FreeFlySaveTime then if self.TrackerForcePos then -- 有强制追踪点 self:SetTrackerVirtualPos(self.TrackerForcePos) else if self.TrackerTarget == nil then --self.TrackerTarget = SystemManager.OnSearchTrackerTarget(self) self:SetTrackerTarget(SystemManager.OnSearchTrackerTarget(self)) end if self.TrackerTarget then if not self.MissileData.IsActualTrack then -- 非实时追踪,以最后位置为准 self.TrackerVirtualPos = self.TrackerTarget.components.move.LogicPos:cloneV2() if self.ExpMissileList then for i,expM in ipairs(self.ExpMissileList) do expM:SetTrackerForceY() end else self.ExpMissile:SetTrackerForceY() end self.TrackerTarget = nil end elseif self.MissileData.FreeFlyMaxTime >= Const_Zero and self.FreeFlyTime >= self.MissileData.FreeFlyMaxTime then self:SetTrackerVirtualPos(self:GetTrackerVirtualPos()) end end self.InFreeFly = false end else if self.TrackerTarget == nil and self.TrackerVirtualPos == nil then --self.TrackerTarget = SystemManager.OnSearchTrackerTarget(self) self:SetTrackerTarget(SystemManager.OnSearchTrackerTarget(self)) if self.TrackerTarget == nil then -- 当直接进入追踪阶段时,没有设置虚拟目标点 self:SetTrackerVirtualPos(self:GetTrackerVirtualPos()) end end self.TrackTime:Add(_tempfixp1:Reset(LuaConst_TimeSpanMs):Mul(timeScale)) --print("tarckTime", tostring(self.TrackTime)) if self.NeedTrackAccelerate and Less(self.TrackTime, 2000) then self.TrackSpeed:Add(_tempfixp1:Reset(LuaConst_TimeSpan):Mul(self.TrackAccelerate)) --print("self.TrackSpeed", tostring(self.TrackSpeed)) end if self.TrackTime >= self.MissileData.TrackFlyTime then self:Cancelled() return else SystemManager.OnMissileUpdate(self) end -- 距离太近要消失 if self.TrackerTarget == nil then local isfar = IsDistanceGreater(self.Pos, self.TrackerVirtualPos, _tempfixp1:Reset(self.TrackSpeed):Mul(LuaConst_TimeSpan):Mul(timeScale):Div(2)) if not isfar then self.Pos:Reset(self.TrackerVirtualPos) -- 为了后续功能一点误差也没有 self:Cancelled() return end elseif self.MissileData.CatchUpDel then local isfar = IsDistanceGreater(self.Pos, self.TrackerTarget.components.move.LogicPos, _tempfixp1:Reset(self.TrackSpeed):Mul(LuaConst_TimeSpan):Mul(timeScale):Div(2)) if not isfar then --local cadis = FP.V2.Distance(self.Pos, self.TrackerTarget.components.move.LogicPos) --print("子弹消失" , self.MissileId , " ||| dis :", tostring(cadis)) self.Pos:Reset(self.TrackerTarget.components.move.LogicPos)-- 为了后续功能一点误差也没有 self:Cancelled() return end end end else if self.CurTime >= self.TotalTime then if self.CurTime >= _tempfixp1:Reset(self.TotalTime):Add(self.MissileData.HideDelayFrame) then self:Cancelled() return else self:WaitCancel() end else SystemManager.OnMissileUpdate(self) end end self.LastTime:Reset(self.CurTime) self.CurTime:Add(_tempfixp1:Reset(LuaConst_TimeSpanMs):Mul(timeScale)) if self.AttackRange then local positionX local positionY local positionZ local forwardX local forwardZ if self:IsHooked() then --如果钩住目标了,走钩子的逻辑 local targetPos = self.hookEntity:GetLogicPos() local startPos = self.hookStartEntity:GetLogicPos() local delta = newV2(0,0) local newPos = newV2(0,0) delta:Reset(targetPos):Sub(startPos) newPos:Reset(delta):Mul(0.5):Add(startPos) local dir = newV2(0,0) dir:Reset(delta) dir:Normalize() positionX = tonumberV1(newPos.x) positionZ = tonumberV1(newPos.z) positionY = AuxUtil.GetHigh(positionX,0,positionZ) positionY = positionY + 0.5 --连线的特效高一点,不然很容易进地里 forwardX, forwardZ = tonumberV2(dir) AttackRange.SetScale(self.MissileId,tonumberV1(self:GetShapeParam1()),tonumberV1(self:GetShapeParam2())) else positionX, positionZ = tonumberV2(self.Pos) positionY = AuxUtil.GetHigh(positionX,0,positionZ) forwardX, forwardZ = tonumberV2(self.MoveDir) end AttackRange.SetForwardAndPostion(self.MissileId,forwardX,forwardZ ,positionX , positionY , positionZ, self:IsHooked()) end -- 低于地面高度,需要判断是否需要销毁 if self.MissileData.HitWalkPlaneDestroy then LuaShoveManager.GetHeightByPos(self.Pos,_tempfixp1) -- 获取地形高度 if self.PosY <= _tempfixp1 then -- 低于地面高度 self:Cancelled() return end end end --[[ 表现的Tick --]] function Missile:OnExpUpdate(deltaTime) if self.ExpMissileList then for i,expM in ipairs(self.ExpMissileList) do expM:ExpUpdate(deltaTime) end else self.ExpMissile:ExpUpdate(deltaTime) end end --missileBuffDataTableSeg是MissileBuffData_Table的元素 function Missile:OnMissileBuffDataAddBuff(missileBuffDataTableSeg) self.missileBuffDataAddedCountDic = self.missileBuffDataAddedCountDic or {} self.missileBuffDataAddedCountDic[missileBuffDataTableSeg] = (self.missileBuffDataAddedCountDic[missileBuffDataTableSeg] or 0) + 1 end --missileBuffDataTableSeg是MissileBuffData_Table的元素 function Missile:GetMissileBuffDataAddedCount(missileBuffDataTableSeg) return self.missileBuffDataAddedCountDic and (self.missileBuffDataAddedCountDic[missileBuffDataTableSeg] or 0) or 0 end function Missile:OnHitEntity(hitEntity) if self.MissileData.IsStartWhenTrigger or self.IsTracker then self.HitEntityIdList[hitEntity._logicInitData.EntityID] = self.CurTime[1] -- 为了省内存用了【1】 end if self:IsHook() and not self:IsHooked() then self:EstablishHook(hitEntity) end self.CurFrameHitEntityCount = self.CurFrameHitEntityCount + 1 table.insert(self.CurFrameHitEntityIdList, hitEntity.EntityID) self.TotalHitEntityCount = self.TotalHitEntityCount + 1 self.lastHitEntity = hitEntity end --[[ 回收 --]] function Missile:Cancelled(noNext) if self.Unload then -- 如果在某个时刻已经把子弹销毁了,那么后面的逻辑就不能再销毁一次了 return end --消失前检测是否需要发射next子弹 if not noNext then local nextInfoType for i,nextInfo in ipairs(self.MissileData.FireMissileData_Table) do nextInfoType = nextInfo.Type if nextInfoType == LuaConst_FireNextType_End or (nextInfoType == LuaConst_FireNextType_CancelAndNoHit and self.TotalHitEntityCount <= 0 ) or (nextInfoType == LuaConst_FireNextType_CancelAndHitted and self.TotalHitEntityCount > 0 ) then SystemManager.CallNextBullet(self, nextInfo, nextInfoType) elseif nextInfoType == LuaConst_FireNextType_CancelAndLastHitFriend then if self.lastHitEntity and self.lastHitEntity:GetCampID() == self.Attacker:GetCampID() then SystemManager.CallNextBullet(self, nextInfo, nextInfoType) end elseif nextInfoType == LuaConst_FireNextType_CancelAndLastHitEnemey then if self.lastHitEntity and CampManager.IsEnemyEntity(self.lastHitEntity, self.Attacker) then SystemManager.CallNextBullet(self, nextInfo, nextInfoType) end end end if self.bounceInfo then -- 此子弹有弹射功能,发射弹射子弹 SystemManager.CallNextBounceBullet(self.Attacker, self, self.bounceInfo, self.lastHitEntity) end end _isWaitCancel = false MissileManager.CollectMissile(self) if self.ExpMissileList then for i,expM in ipairs(self.ExpMissileList) do expM:OnCancelled(self) end else self.ExpMissile:OnCancelled(self) end if self:IsHooked() then private.hookMissileCount = private.hookMissileCount - 1 BattleEventSystem.AfterHookMissileBreak(self) end self.hookEntity = nil if self.AttackRange then AttackRange.DisposeBullet(self.MissileId) --new self.AttackRange = nil end self.Unload = true BattleEventSystem.OnEntityMissileCancel(self.Attacker, self.MissileData, self) end --[[ 触发 OnMissileWaitCancel 事件 --]] function Missile:WaitCancel() if _isWaitCancel then return end --检测是否需要发射next子弹 for i,nextInfo in ipairs(self.MissileData.FireMissileData_Table) do if nextInfo.Type == LuaConst_FireNextType_WaitCancle then SystemManager.CallNextBullet(self, nextInfo, LuaConst_FireNextType_WaitCancle) end end --SystemManager.OnMissileWaitCancel() _isWaitCancel = true if self.ExpMissileList then for i,expM in ipairs(self.ExpMissileList) do expM:WaitCancelled(self) end else self.ExpMissile:WaitCancelled(self) end if self.AttackRange then AttackRange.DisposeBullet(self.MissileId) --new end end function Missile:GetTrackerVirtualPos() --选择施法者正方向一定距离 if self.MissileData.AimVirtualType == 1 then return self.Attacker.components.move.LogicPos + self.Attacker.components.move.LogicRotateDir * self.MissileData.AimVirtualRadius end --选择施法者为圆心的一定半径的选项区域随机 local randomDis = LogicRandom.GetRangeValue(nil, 0, self.MissileData.AimVirtualRadius ,20) local randomDir = newV2(LogicRandom.GetRangeValue(nil, -1,1 ,21), LogicRandom.GetRangeValue(nil,-1,1 ,22)) Normalize(randomDir) --print("randomDis", tostring(randomDis), "randomDir", tostring(randomDir.x), tostring(randomDir.z)) local finalPos = self.Attacker.components.move.LogicPos + randomDir * randomDis --print("finalPos", tostring(finalPos.x), tostring(finalPos.z)) return finalPos end function Missile:SetTrackerVirtualPos(pos) self.TrackerVirtualPos = pos LuaShoveManager.GetHeightByPos(self.TrackerVirtualPos,_tempfixp1) local expY = self.MissileData.AimVirtualHeight + FP.tonumberV1(_tempfixp1) self.ExpMissile:SetTrackerForceY(expY) end function Missile:GetShapeParam1() return self.overrideShapeParam1 or self.MissileData.ShapeParam1 end function Missile:GetShapeParam2() return self.overrideShapeParam2 or self.MissileData.ShapeParam2 end function Missile:GetShapeParam3() return self.overrideShapeParam3 or self.MissileData.ShapeParam3 end function Missile:OverrideShapeParam1(value) self.overrideShapeParam1 = value end function Missile:OverrideShapeParam2(value) self.overrideShapeParam2 = value end function Missile:OverrideShapeParam3(value) self.overrideShapeParam3 = value end function Missile:OverrideAngleSpeedAdd(value, isAdd) if self.overrideAngleSpeed == nil then self.overrideAngleSpeed = newV1(0) self.overrideAngleSpeed:Reset(self.MissileData.AngleSpeed) end if isAdd then self.overrideAngleSpeed:Add(value) else self.overrideAngleSpeed:Reset(value) end end --↓↓↓钩子特性支持↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ --是否具有钩子特性 function Missile:IsHook() return self.MissileData.IsHook end --是否钩住目标了(隐含IsHook()为true,因为只有这种子弹,hookEntity才可能被设值) function Missile:IsHooked() return self.hookEntity ~= nil end --返回FP function Missile:GetHookLength() return self:GetShapeParam2() end function Missile:ShouldBreakHook() local should = false local isTooFarBreak = false if self.hookEntity:IsAlive() and self.hookEntity:IsActive() and self.hookStartEntity:IsAlive() and self.hookStartEntity:IsActive() then should = self:GetShapeParam3() > Const_Zero and self.hookEntity:GetLogicPos():Distance(self.hookStartEntity:GetLogicPos()) > self:GetShapeParam3() --这里为true是因为距离过远,断开钩子 isTooFarBreak = should else should = true end return should,isTooFarBreak end function Missile:EstablishHook(hitEntity, hitEntity2) self.hookEntity = hitEntity self.hookStartEntity = hitEntity2 or self.Attacker if self.ExpMissileList then for i,expM in ipairs(self.ExpMissileList) do expM:OnEstablishHook() end else self.ExpMissile:OnEstablishHook() end private.hookMissileCount = private.hookMissileCount + 1 if private.hookMissileCount > 1000 then private.hookMissileCount = 900 --防止每帧都报,这样5秒报一次就行 logging.error(LTAG.ERROR, "HYW : 致命错误! 钩子子弹数量大于1000,哪里肯定出错了! MissileId : ", self.MissileData.ID) end BattleEventSystem.AfterHookMissileHooked(self) end function Missile:BreakHook(isTooFarBreak) self:Cancelled(true) if isTooFarBreak then BattleEventSystem.AfterHookMissileTooFarBreak(self) end end --↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ return Missile