internal void Clean() { for (int j = 0; j < CleanUp.Count; j++) { var p = CleanUp[j]; for (int i = 0; i < p.VrPros.Count; i++) { var virtInfo = p.VrPros[i]; virtInfo.Info.Clean(Session.Tick); VirtInfoPool.Return(virtInfo.Info); } p.VrPros.Clear(); if (p.DynamicGuidance) { DynTrees.UnregisterProjectile(p); } p.PruningProxyId = -1; p.Info.Clean(Session.Tick); ProjectilePool.Push(p); ActiveProjetiles.Remove(p); } CleanUp.Clear(); }
internal void ProjectileClose() { if (GenerateShrapnel && Info.Age >= Info.ConsumableDef.Const.MinArmingTime) { SpawnShrapnel(); } for (int i = 0; i < Watchers.Count; i++) { Watchers[i].DeadProjectiles.Add(this); } Watchers.Clear(); foreach (var seeker in Seekers) { seeker.Info.Target.Reset(Info.System.Session.Tick, Target.States.ProjectileClosed); } Seekers.Clear(); if (EnableAv && Info.AvShot.ForceHitParticle) { Info.AvShot.HitEffects(true); } State = ProjectileState.Dead; var detInfo = Info.ConsumableDef.AreaEffect.Detonation; var afInfo = Info.ConsumableDef.AreaEffect; var detExp = !afInfo.Explosions.NoVisuals && (afInfo.AreaEffect == AreaEffectType.Explosive || afInfo.AreaEffect == AreaEffectType.Radiant && afInfo.Explosions.CustomParticle != string.Empty) && detInfo.DetonateOnEnd && (!detInfo.ArmOnlyOnHit || Info.ObjectsHit > 0); if (EnableAv) { if (ModelState == EntityState.Exists) { ModelState = EntityState.None; } if (!Info.AvShot.Active) { Info.System.Session.Av.AvShotPool.Return(Info.AvShot); } else { Info.AvShot.EndState = new AvClose { EndPos = Position, Dirty = true, DetonateFakeExp = detExp } }; } else if (Info.ConsumableDef.Const.VirtualBeams) { for (int i = 0; i < VrPros.Count; i++) { var vp = VrPros[i]; if (!vp.AvShot.Active) { Info.System.Session.Av.AvShotPool.Return(vp.AvShot); } else { vp.AvShot.EndState = new AvClose { EndPos = Position, Dirty = true, DetonateFakeExp = detExp } }; Info.System.Session.Projectiles.VirtInfoPool.Return(vp); } VrPros.Clear(); } if (DynamicGuidance) { DynTrees.UnregisterProjectile(this); } PruningProxyId = -1; Info.Clean(); }
internal void EwarEffects() { switch (Info.ConsumableDef.Const.AreaEffect) { case AreaEffectType.AntiSmart: var eWarSphere = new BoundingSphereD(Position, Info.ConsumableDef.Const.AreaEffectSize); DynTrees.GetAllProjectilesInSphere(Info.System.Session, ref eWarSphere, EwaredProjectiles, false); for (int j = 0; j < EwaredProjectiles.Count; j++) { var netted = EwaredProjectiles[j]; if (netted.Info.Target.FiringCube.CubeGrid.IsSameConstructAs(Info.Target.FiringCube.CubeGrid) || netted.Info.Target.IsProjectile) { continue; } if (Info.WeaponRng.ClientProjectileRandom.NextDouble() * 100f < Info.ConsumableDef.Const.PulseChance || !Info.ConsumableDef.Const.Pulse) { Info.BaseEwarPool -= Info.ConsumableDef.Const.AreaEffectDamage; if (Info.BaseEwarPool <= 0) { Info.EwarActive = true; netted.Info.Target.Projectile = this; netted.Info.Target.IsProjectile = true; Seekers.Add(netted); } } Info.WeaponRng.ClientProjectileCurrentCounter++; } EwaredProjectiles.Clear(); return; case AreaEffectType.PushField: if (Info.EwarAreaPulse && Info.WeaponRng.ClientProjectileRandom.NextDouble() * 100f <= Info.ConsumableDef.Const.PulseChance || !Info.ConsumableDef.Const.Pulse) { Info.EwarActive = true; } break; case AreaEffectType.PullField: if (Info.EwarAreaPulse && Info.WeaponRng.ClientProjectileRandom.NextDouble() * 100f <= Info.ConsumableDef.Const.PulseChance || !Info.ConsumableDef.Const.Pulse) { Info.EwarActive = true; } break; case AreaEffectType.JumpNullField: if (Info.EwarAreaPulse && Info.WeaponRng.ClientProjectileRandom.NextDouble() * 100f <= Info.ConsumableDef.Const.PulseChance || !Info.ConsumableDef.Const.Pulse) { Info.EwarActive = true; } break; case AreaEffectType.AnchorField: if (Info.EwarAreaPulse && Info.WeaponRng.ClientProjectileRandom.NextDouble() * 100f <= Info.ConsumableDef.Const.PulseChance || !Info.ConsumableDef.Const.Pulse) { Info.EwarActive = true; } break; case AreaEffectType.EnergySinkField: if (Info.EwarAreaPulse && Info.WeaponRng.ClientProjectileRandom.NextDouble() * 100f <= Info.ConsumableDef.Const.PulseChance || !Info.ConsumableDef.Const.Pulse) { Info.EwarActive = true; } break; case AreaEffectType.EmpField: if (Info.EwarAreaPulse && Info.WeaponRng.ClientProjectileRandom.NextDouble() * 100f <= Info.ConsumableDef.Const.PulseChance || !Info.ConsumableDef.Const.Pulse) { Info.EwarActive = true; } break; case AreaEffectType.OffenseField: if (Info.EwarAreaPulse && Info.WeaponRng.ClientProjectileRandom.NextDouble() * 100f <= Info.ConsumableDef.Const.PulseChance || !Info.ConsumableDef.Const.Pulse) { Info.EwarActive = true; } break; case AreaEffectType.NavField: if (!Info.ConsumableDef.Const.Pulse || Info.EwarAreaPulse && Info.WeaponRng.ClientProjectileRandom.NextDouble() * 100f <= Info.ConsumableDef.Const.PulseChance) { Info.EwarActive = true; } break; case AreaEffectType.DotField: if (Info.EwarAreaPulse && Info.WeaponRng.ClientProjectileRandom.NextDouble() * 100f <= Info.ConsumableDef.Const.PulseChance || !Info.ConsumableDef.Const.Pulse) { Info.EwarActive = true; } break; } Info.WeaponRng.ClientProjectileCurrentCounter++; }
internal void Start() { Position = Info.Origin; AccelDir = Info.Direction; var cameraStart = Info.System.Session.CameraPos; Vector3D.DistanceSquared(ref cameraStart, ref Info.Origin, out DistanceFromCameraSqr); GenerateShrapnel = Info.ConsumableDef.Const.ShrapnelId > -1; var probability = Info.ConsumableDef.AmmoGraphics.VisualProbability; EnableAv = !Info.ConsumableDef.Const.VirtualBeams && !Info.System.Session.DedicatedServer && DistanceFromCameraSqr <= Info.System.Session.SyncDistSqr && (probability >= 1 || probability >= MyUtils.GetRandomDouble(0.0f, 1f)); ModelState = EntityState.None; LastEntityPos = Position; LastHitEntVel = null; Info.AvShot = null; Info.Age = -1; ChaseAge = 0; NewTargets = 0; ZombieLifeTime = 0; LastOffsetTime = 0; PruningProxyId = -1; EntitiesNear = false; CachedPlanetHit = false; PositionChecked = false; MineSeeking = false; MineActivated = false; MineTriggered = false; LinePlanetCheck = false; AtMaxRange = false; ShieldBypassed = false; FakeGravityNear = false; HadTarget = false; WasTracking = false; Intersecting = false; EndStep = 0; Info.PrevDistanceTraveled = 0; Info.DistanceTraveled = 0; PrevEndPointToCenterSqr = double.MaxValue; CachedId = Info.MuzzleId == -1 ? Info.WeaponCache.VirutalId : Info.MuzzleId; Guidance = Info.ConsumableDef.Trajectory.Guidance; DynamicGuidance = Guidance != GuidanceType.None && Guidance != GuidanceType.TravelTo && !Info.ConsumableDef.Const.IsBeamWeapon && Info.EnableGuidance; if (DynamicGuidance) { DynTrees.RegisterProjectile(this); } FeelsGravity = Info.ConsumableDef.Const.FeelsGravity; Info.MyPlanet = Info.Ai.MyPlanet; if (!Info.System.Session.VoxelCaches.TryGetValue(Info.UniqueMuzzleId, out Info.VoxelCache)) { Log.Line($"ProjectileStart VoxelCache Failure with Id:{Info.UniqueMuzzleId} BlockMarked:{Info.Target.FiringCube?.MarkedForClose}, setting to default cache:"); Info.VoxelCache = Info.System.Session.VoxelCaches[ulong.MaxValue]; } if (Info.MyPlanet != null) { Info.VoxelCache.PlanetSphere.Center = Info.Ai.ClosestPlanetCenter; } Info.MyShield = Info.Ai.MyShield; Info.InPlanetGravity = Info.Ai.InPlanetGravity; Info.AiVersion = Info.Ai.Version; Info.Ai.ProjectileTicker = Info.Ai.Session.Tick; if (Guidance == GuidanceType.Smart && DynamicGuidance) { SmartsOn = true; MaxChaseTime = Info.ConsumableDef.Const.MaxChaseTime; SmartSlot = Info.WeaponRng.ClientProjectileRandom.Next(10); Info.WeaponRng.ClientProjectileCurrentCounter++; } else { MaxChaseTime = int.MaxValue; SmartsOn = false; SmartSlot = 0; } if (Info.Target.IsProjectile) { OriginTargetPos = Info.Target.Projectile.Position; Info.Target.Projectile.Seekers.Add(this); } else if (Info.Target.Entity != null) { OriginTargetPos = Info.Target.Entity.PositionComp.WorldAABB.Center; } else { OriginTargetPos = Vector3D.Zero; } LockedTarget = !Vector3D.IsZero(OriginTargetPos); if (SmartsOn && Info.ConsumableDef.Const.TargetOffSet && (LockedTarget || Info.Target.IsFakeTarget)) { OffSetTarget(); OffsetSqr = Info.ConsumableDef.Trajectory.Smarts.Inaccuracy * Info.ConsumableDef.Trajectory.Smarts.Inaccuracy; } else { TargetOffSet = Vector3D.Zero; OffsetSqr = 0; } PrevTargetOffset = Vector3D.Zero; var targetSpeed = (float)(!Info.ConsumableDef.Const.IsBeamWeapon ? Info.ConsumableDef.Trajectory.DesiredSpeed : Info.MaxTrajectory * MyEngineConstants.UPDATE_STEPS_PER_SECOND); if (Info.ConsumableDef.Const.SpeedVariance && !Info.ConsumableDef.Const.IsBeamWeapon) { var min = Info.ConsumableDef.Trajectory.SpeedVariance.Start; var max = Info.ConsumableDef.Trajectory.SpeedVariance.End; var speedVariance = (float)Info.WeaponRng.ClientProjectileRandom.NextDouble() * (max - min) + min; Info.WeaponRng.ClientProjectileCurrentCounter++; DesiredSpeed = targetSpeed + speedVariance; } else { DesiredSpeed = targetSpeed; } float variance = 0; if (Info.ConsumableDef.Const.RangeVariance) { var min = Info.ConsumableDef.Trajectory.RangeVariance.Start; var max = Info.ConsumableDef.Trajectory.RangeVariance.End; variance = (float)Info.WeaponRng.ClientProjectileRandom.NextDouble() * (max - min) + min; Info.MaxTrajectory -= variance; Info.WeaponRng.ClientProjectileCurrentCounter++; } if (Vector3D.IsZero(PredictedTargetPos)) { PredictedTargetPos = Position + (AccelDir * Info.MaxTrajectory); } PrevTargetPos = PredictedTargetPos; PrevTargetVel = Vector3D.Zero; Info.ObjectsHit = 0; Info.BaseHealthPool = Info.ConsumableDef.Health; Info.BaseEwarPool = Info.ConsumableDef.Health; Info.TracerLength = Info.ConsumableDef.Const.TracerLength <= Info.MaxTrajectory ? Info.ConsumableDef.Const.TracerLength : Info.MaxTrajectory; MaxTrajectorySqr = Info.MaxTrajectory * Info.MaxTrajectory; if (!Info.IsShrapnel) { StartSpeed = Info.ShooterVel; } MoveToAndActivate = LockedTarget && !Info.ConsumableDef.Const.IsBeamWeapon && Guidance == GuidanceType.TravelTo; if (MoveToAndActivate) { var distancePos = !Vector3D.IsZero(PredictedTargetPos) ? PredictedTargetPos : OriginTargetPos; if (variance > 0) { var forward = Info.WeaponRng.ClientProjectileRandom.Next(100) < 50; Info.WeaponRng.ClientProjectileCurrentCounter++; distancePos = forward ? distancePos + (AccelDir * variance) : distancePos + (-AccelDir * variance); } Vector3D.DistanceSquared(ref Info.Origin, ref distancePos, out DistanceToTravelSqr); } else { DistanceToTravelSqr = MaxTrajectorySqr; } PickTarget = Info.ConsumableDef.Trajectory.Smarts.OverideTarget && !Info.Target.IsFakeTarget && !Info.LockOnFireState; if (PickTarget || LockedTarget) { NewTargets++; } var staticIsInRange = Info.Ai.ClosestStaticSqr * 0.5 < MaxTrajectorySqr; var pruneStaticCheck = Info.Ai.ClosestPlanetSqr * 0.5 < MaxTrajectorySqr || Info.Ai.StaticGridInRange; PruneQuery = (DynamicGuidance && pruneStaticCheck) || FeelsGravity && staticIsInRange || !DynamicGuidance && !FeelsGravity && staticIsInRange ? MyEntityQueryType.Both : MyEntityQueryType.Dynamic; if (Info.Ai.PlanetSurfaceInRange && Info.Ai.ClosestPlanetSqr <= MaxTrajectorySqr) { LinePlanetCheck = true; PruneQuery = MyEntityQueryType.Both; } if (DynamicGuidance && PruneQuery == MyEntityQueryType.Dynamic && staticIsInRange) { CheckForNearVoxel(60); } var accelPerSec = Info.ConsumableDef.Trajectory.AccelPerSec; ConstantSpeed = accelPerSec <= 0; AccelInMetersPerSec = accelPerSec > 0 ? accelPerSec : DesiredSpeed; var desiredSpeed = (AccelDir * DesiredSpeed); var relativeSpeedCap = StartSpeed + desiredSpeed; MaxVelocity = relativeSpeedCap; MaxSpeed = MaxVelocity.Length(); MaxSpeedSqr = MaxSpeed * MaxSpeed; DeltaVelocityPerTick = accelPerSec * StepConst; AccelVelocity = (AccelDir * DeltaVelocityPerTick); if (ConstantSpeed) { Velocity = MaxVelocity; VelocityLengthSqr = MaxSpeed * MaxSpeed; } else { Velocity = StartSpeed + AccelVelocity; } if (Info.IsShrapnel) { Vector3D.Normalize(ref Velocity, out Info.Direction); } InitalStep = !Info.IsShrapnel && ConstantSpeed ? desiredSpeed * StepConst : Velocity * StepConst; TravelMagnitude = Velocity * StepConst; FieldTime = Info.ConsumableDef.Const.Ewar || Info.ConsumableDef.Const.IsMine ? Info.ConsumableDef.Trajectory.FieldTime : 0; State = !Info.ConsumableDef.Const.IsBeamWeapon ? ProjectileState.Alive : ProjectileState.OneAndDone; if (EnableAv) { Info.AvShot = Info.System.Session.Av.AvShotPool.Get(); Info.AvShot.Init(Info, AccelInMetersPerSec * StepConst, MaxSpeed, ref AccelDir); Info.AvShot.SetupSounds(DistanceFromCameraSqr); //Pool initted sounds per Projectile type... this is expensive if (Info.ConsumableDef.Const.HitParticle && !Info.ConsumableDef.Const.IsBeamWeapon || Info.ConsumableDef.Const.AreaEffect == AreaEffectType.Explosive && !Info.ConsumableDef.AreaEffect.Explosions.NoVisuals && Info.ConsumableDef.Const.AreaEffectSize > 0 && Info.ConsumableDef.Const.AreaEffectDamage > 0) { var hitPlayChance = Info.ConsumableDef.AmmoGraphics.Particles.Hit.Extras.HitPlayChance; Info.AvShot.HitParticleActive = hitPlayChance >= 1 || hitPlayChance >= MyUtils.GetRandomDouble(0.0f, 1f); } Info.AvShot.FakeExplosion = Info.AvShot.HitParticleActive && Info.ConsumableDef.Const.AreaEffect == AreaEffectType.Explosive && Info.ConsumableDef.AmmoGraphics.Particles.Hit.Name == string.Empty; } if (!Info.ConsumableDef.Const.PrimeModel && !Info.ConsumableDef.Const.TriggerModel) { ModelState = EntityState.None; } else { if (EnableAv) { ModelState = EntityState.Exists; double triggerModelSize = 0; double primeModelSize = 0; if (Info.ConsumableDef.Const.TriggerModel) { triggerModelSize = Info.AvShot.TriggerEntity.PositionComp.WorldVolume.Radius; } if (Info.ConsumableDef.Const.PrimeModel) { primeModelSize = Info.AvShot.PrimeEntity.PositionComp.WorldVolume.Radius; } var largestSize = triggerModelSize > primeModelSize ? triggerModelSize : primeModelSize; Info.AvShot.ModelSphereCurrent.Radius = largestSize * 2; } } if (EnableAv) { LineOrNotModel = Info.ConsumableDef.Const.DrawLine || ModelState == EntityState.None && Info.ConsumableDef.Const.AmmoParticle; Info.AvShot.ModelOnly = !LineOrNotModel && ModelState == EntityState.Exists; } }