/// <summary>
        /// This method will calculate if there is valid line of sight between the explosion origin and the specific Part
        /// In order to avoid collisions with the same missile part, It will not take into account those parts beloging to same vessel that contains the explosive part
        /// </summary>
        /// <param name="part"></param>
        /// <param name="explosivePart"></param>
        /// <param name="hit"> out property with the actual hit</param>
        /// <returns></returns>
        private bool IsInLineOfSight(Part part, Part explosivePart, out RaycastHit hit, out float distance, out List <Tuple <float, float, float> > intermediateParts)
        {
            Ray partRay  = new Ray(Position, part.transform.position - Position);
            var hitCount = Physics.RaycastNonAlloc(partRay, lineOfSightHits, Range, 9076737);

            if (hitCount == lineOfSightHits.Length) // If there's a whole bunch of stuff in the way (unlikely), then we need to increase the size of our hits buffer.
            {
                lineOfSightHits = Physics.RaycastAll(partRay, Range, 9076737);
                hitCount        = lineOfSightHits.Length;
            }
            intermediateParts = new List <Tuple <float, float, float> >();
            using (var hitsEnu = lineOfSightHits.Take(hitCount).OrderBy(x => x.distance).GetEnumerator())
                while (hitsEnu.MoveNext())
                {
                    Part partHit = hitsEnu.Current.collider.GetComponentInParent <Part>();
                    if (partHit == null)
                    {
                        continue;
                    }
                    if (ProjectileUtils.IsIgnoredPart(partHit))
                    {
                        continue;                                         // Ignore ignored parts.
                    }
                    hit      = hitsEnu.Current;
                    distance = hit.distance;
                    if (partHit == part)
                    {
                        return(true);
                    }
                    if (partHit != part)
                    {
                        // ignoring collisions against the explosive
                        if (explosivePart != null && partHit.vessel == explosivePart.vessel)
                        {
                            continue;
                        }
                        if (FlightGlobals.currentMainBody != null && hit.collider.gameObject == FlightGlobals.currentMainBody.gameObject)
                        {
                            return(false);                                                                                                              // Terrain hit. Full absorption. Should avoid NREs in the following.
                        }
                        var partHP     = partHit.Damage();
                        var partArmour = partHit.GetArmorThickness();
                        if (partHP > 0) // Ignore parts that are already dead but not yet removed from the game.
                        {
                            intermediateParts.Add(new Tuple <float, float, float>(hit.distance, partHP, partArmour));
                        }
                    }
                }

            hit      = new RaycastHit();
            distance = 0;
            return(false);
        }
        private List <BlastHitEvent> ProcessingBlastSphere()
        {
            explosionEventsPreProcessing.Clear();
            explosionEventsPartsAdded.Clear();
            explosionEventsBuildingAdded.Clear();
            explosionEventsVesselsHitByMissiles.Clear();

            string sourceVesselName = null;

            if (BDACompetitionMode.Instance)
            {
                switch (ExplosionSource)
                {
                case ExplosionSourceType.Missile:
                    var explosivePart = ExplosivePart ? ExplosivePart.FindModuleImplementing <BDExplosivePart>() : null;
                    sourceVesselName = explosivePart ? explosivePart.sourcevessel.GetName() : SourceVesselName;
                    break;

                case ExplosionSourceType.Bullet:
                    sourceVesselName = SourceVesselName;
                    break;

                default:
                    break;
                }
            }
            var overlapSphereColliderCount = Physics.OverlapSphereNonAlloc(Position, Range, overlapSphereColliders, 9076737);

            if (overlapSphereColliderCount == overlapSphereColliders.Length)
            {
                overlapSphereColliders     = Physics.OverlapSphere(Position, Range, 9076737);
                overlapSphereColliderCount = overlapSphereColliders.Length;
            }
            using (var hitCollidersEnu = overlapSphereColliders.Take(overlapSphereColliderCount).GetEnumerator())
            {
                while (hitCollidersEnu.MoveNext())
                {
                    if (hitCollidersEnu.Current == null)
                    {
                        continue;
                    }

                    Part partHit = hitCollidersEnu.Current.GetComponentInParent <Part>();
                    if (partHit == null)
                    {
                        continue;
                    }
                    if (ProjectileUtils.IsIgnoredPart(partHit))
                    {
                        continue;                                         // Ignore ignored parts.
                    }
                    if (partHit != null && partHit.mass > 0 && !explosionEventsPartsAdded.Contains(partHit))
                    {
                        var damaged = ProcessPartEvent(partHit, sourceVesselName, explosionEventsPreProcessing, explosionEventsPartsAdded);
                        // If the explosion derives from a missile explosion, count the parts damaged for missile hit scores.
                        if (damaged && ExplosionSource == ExplosionSourceType.Missile && BDACompetitionMode.Instance)
                        {
                            if (sourceVesselName != null && BDACompetitionMode.Instance.Scores.ContainsKey(sourceVesselName)) // Check that the source vessel is in the competition.
                            {
                                var damagedVesselName = partHit.vessel != null?partHit.vessel.GetName() : null;

                                if (damagedVesselName != null && damagedVesselName != sourceVesselName && BDACompetitionMode.Instance.Scores.ContainsKey(damagedVesselName)) // Check that the damaged vessel is in the competition and isn't the source vessel.
                                {
                                    if (BDACompetitionMode.Instance.Scores[damagedVesselName].missilePartDamageCounts.ContainsKey(sourceVesselName))
                                    {
                                        ++BDACompetitionMode.Instance.Scores[damagedVesselName].missilePartDamageCounts[sourceVesselName];
                                    }
                                    else
                                    {
                                        BDACompetitionMode.Instance.Scores[damagedVesselName].missilePartDamageCounts[sourceVesselName] = 1;
                                    }
                                    if (!BDACompetitionMode.Instance.Scores[damagedVesselName].everyoneWhoHitMeWithMissiles.Contains(sourceVesselName))
                                    {
                                        BDACompetitionMode.Instance.Scores[damagedVesselName].everyoneWhoHitMeWithMissiles.Add(sourceVesselName);
                                    }
                                    ++BDACompetitionMode.Instance.Scores[sourceVesselName].totalDamagedPartsDueToMissiles;
                                    BDACompetitionMode.Instance.Scores[damagedVesselName].lastMissileHitTime             = Planetarium.GetUniversalTime();
                                    BDACompetitionMode.Instance.Scores[damagedVesselName].lastPersonWhoHitMeWithAMissile = sourceVesselName;
                                    if (explosionEventsVesselsHitByMissiles.ContainsKey(damagedVesselName))
                                    {
                                        ++explosionEventsVesselsHitByMissiles[damagedVesselName];
                                    }
                                    else
                                    {
                                        explosionEventsVesselsHitByMissiles[damagedVesselName] = 1;
                                    }
                                    if (BDArmorySettings.REMOTE_LOGGING_ENABLED)
                                    {
                                        BDAScoreService.Instance.TrackMissileParts(sourceVesselName, damagedVesselName, 1);
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        DestructibleBuilding building = hitCollidersEnu.Current.GetComponentInParent <DestructibleBuilding>();

                        if (building != null && !explosionEventsBuildingAdded.Contains(building))
                        {
                            ProcessBuildingEvent(building, explosionEventsPreProcessing, explosionEventsBuildingAdded);
                        }
                    }
                }
            }
            if (explosionEventsVesselsHitByMissiles.Count > 0)
            {
                string message = "";
                foreach (var vesselName in explosionEventsVesselsHitByMissiles.Keys)
                {
                    message += (message == "" ? "" : " and ") + vesselName + " had " + explosionEventsVesselsHitByMissiles[vesselName];
                }
                message += " parts damaged due to missile strike" + (SourceWeaponName != null ? " (" + SourceWeaponName + ")" : "") + (sourceVesselName != null ? " from " + sourceVesselName : "") + ".";
                BDACompetitionMode.Instance.competitionStatus.Add(message);
                // Note: damage hasn't actually been applied to the parts yet, just assigned as events, so we can't know if they survived.
                foreach (var vesselName in explosionEventsVesselsHitByMissiles.Keys) // Note: sourceVesselName is already checked for being in the competition before damagedVesselName is added to explosionEventsVesselsHitByMissiles, so we don't need to check it here.
                {
                    if (BDACompetitionMode.Instance.Scores[vesselName].missileHitCounts.ContainsKey(sourceVesselName))
                    {
                        ++BDACompetitionMode.Instance.Scores[vesselName].missileHitCounts[sourceVesselName];
                    }
                    else
                    {
                        BDACompetitionMode.Instance.Scores[vesselName].missileHitCounts[sourceVesselName] = 1;
                    }
                }
            }
            return(explosionEventsPreProcessing);
        }
        private bool ProximityAirDetonation(float distanceFromStart)
        {
            bool detonate = false;

            if (distanceFromStart <= 500f)
            {
                return(false);
            }

            if (!explosive || tntMass <= 0)
            {
                return(false);
            }

            if (airDetonation)
            {
                if (distanceFromStart > maxAirDetonationRange || distanceFromStart > defaultDetonationRange)
                {
                    return(detonate = true);
                }
            }
            if (proximityDetonation)
            {
                using (var hitsEnu = Physics.OverlapSphere(transform.position, detonationRange, 557057).AsEnumerable().GetEnumerator())
                {
                    while (hitsEnu.MoveNext())
                    {
                        if (hitsEnu.Current == null)
                        {
                            continue;
                        }

                        try
                        {
                            Part partHit = hitsEnu.Current.GetComponentInParent <Part>();
                            if (partHit == null)
                            {
                                continue;
                            }
                            if (partHit.vessel == sourceVessel)
                            {
                                continue;
                            }
                            if (ProjectileUtils.IsIgnoredPart(partHit))
                            {
                                continue;                                         // Ignore ignored parts.
                            }
                            if (BDArmorySettings.DRAW_DEBUG_LABELS)
                            {
                                Debug.Log("[BDArmory.PooledBullet]: Bullet proximity sphere hit | Distance overlap = " + detonationRange + "| Part name = " + partHit.name);
                            }

                            return(detonate = true);
                        }
                        catch (Exception e)
                        {
                            // ignored
                            Debug.LogWarning("[BDArmory.PooledBullet]: Exception thrown in ProximityAirDetonation: " + e.Message + "\n" + e.StackTrace);
                        }
                    }
                }
            }

            return(detonate);
        }
        void Detonate()                                                                                          //borrowed from Stockalike Project Orion
        {
            if (hasDetonated || FlightGlobals.currentMainBody == null || VesselSpawner.Instance.vesselsSpawning) // Don't trigger on scene changes or during spawning.
            {
                return;
            }
            if (BDArmorySettings.DRAW_DEBUG_LABELS)
            {
                Debug.Log("[BDArmory.NukeTest]: Running Detonate() on nerva in vessel " + Sourcevessel);
            }
            //affect any nearby parts/vessels that aren't the source vessel

            Dictionary <string, int> vesselsHitByMissiles = new Dictionary <string, int>();

            using (var blastHits = Physics.OverlapSphere(part.transform.position, thermalRadius, 9076737).AsEnumerable().GetEnumerator())
            {
                partsHit.Clear();
                while (blastHits.MoveNext())
                {
                    if (blastHits.Current == null)
                    {
                        continue;
                    }
                    if (blastHits.Current.gameObject == FlightGlobals.currentMainBody.gameObject)
                    {
                        continue;                                                                           // Ignore terrain hits.
                    }
                    Part partHit = blastHits.Current.GetComponentInParent <Part>();
                    if (partHit == null)
                    {
                        continue;
                    }
                    if (ProjectileUtils.IsIgnoredPart(partHit))
                    {
                        continue;                                         // Ignore ignored parts.
                    }
                    if (partsHit.Contains(partHit))
                    {
                        continue;                             // Don't hit the same part multiple times.
                    }
                    partsHit.Add(partHit);
                    if (partHit != null && partHit.mass > 0)
                    {
                        var   distToG0      = Math.Max((part.transform.position - partHit.transform.position).magnitude, 1f);
                        float radiativeArea = !double.IsNaN(partHit.radiativeArea) ? (float)partHit.radiativeArea : partHit.GetArea();
                        if (BDArmorySettings.DRAW_DEBUG_LABELS && double.IsNaN(partHit.radiativeArea))
                        {
                            Debug.Log("[BDArmory.NukeTest]: radiative area of part " + partHit + " was NaN, using approximate area " + radiativeArea + " instead.");
                        }
                        //if (partHit.vessel != this.vessel)
                        if (partHit != part)
                        {
                            partHit.skinTemperature += fluence * 3370000000 / (4 * Math.PI * Math.Pow(distToG0, 2.0)) * radiativeArea / 2; // Fluence scales linearly w/ yield, 1 Kt will produce between 33 TJ and 337 kJ at 0-1000m,
                        } // everything gets heated via atmosphere

                        Ray        LoSRay = new Ray(part.transform.position, partHit.transform.position - part.transform.position);
                        RaycastHit hit;
                        if (Physics.Raycast(LoSRay, out hit, distToG0, 9076737)) // only add impulse to parts with line of sight to detonation
                        {
                            KerbalEVA eva         = hit.collider.gameObject.GetComponentUpwards <KerbalEVA>();
                            Part      p           = eva ? eva.part : hit.collider.gameObject.GetComponentInParent <Part>();
                            float     blastDamage = 100;
                            if (p == partHit)
                            {
                                //if (p.vessel != this.vessel)
                                if (p != part)
                                {
                                    // Forces
                                    if (p.rb != null && p.rb.mass > 0) // Don't apply forces to physicsless parts.
                                    {
                                        var blastImpulse = Mathf.Pow(3.01f * 1100f / distToG0, 1.25f) * 6.894f * lastValidAtmDensity * yieldCubeRoot * radiativeArea / 3f;
                                        // Math.Pow(Math.Pow(Math.Pow(9.54e-3 * 2200.0 / distToG0, 1.95), 4.0) + Math.Pow(Math.Pow(3.01 * 1100.0 / distToG0, 1.25), 4.0), 0.25) * 6.894 * vessel.atmDensity * Math.Pow(yield, 1.0 / 3.0) * partHit.radiativeArea / 3.0; //assuming a 0.05 kT yield
                                        if (float.IsNaN(blastImpulse))
                                        {
                                            Debug.LogWarning("[BDArmory.NukeTest]: blast impulse is NaN. distToG0: " + distToG0 + ", vessel: " + vessel + ", atmDensity: " + lastValidAtmDensity + ", yield^(1/3): " + yieldCubeRoot + ", partHit: " + partHit + ", radiativeArea: " + radiativeArea);
                                        }
                                        else
                                        {
                                            if (BDArmorySettings.DRAW_DEBUG_LABELS)
                                            {
                                                Debug.Log("[BDArmory.NukeTest]: Applying " + blastImpulse.ToString("0.0") + " impulse to " + p + " of mass " + p.mass + " at distance " + distToG0 + "m");
                                            }
                                            p.rb.AddForceAtPosition((partHit.transform.position - part.transform.position).normalized * (float)blastImpulse, partHit.transform.position, ForceMode.Impulse);
                                        }
                                    }

                                    // Damage
                                    blastDamage = ((float)((yield * 3370000000) / (4f * Mathf.PI * distToG0 * distToG0) * (radiativeArea / 2f)));
                                    if (float.IsNaN(blastDamage))
                                    {
                                        Debug.LogWarning("[BDArmory.NukeTest]: blast damage is NaN. distToG0: " + distToG0 + ", yield: " + yield + ", part: " + partHit + ", radiativeArea: " + radiativeArea);
                                        continue;
                                    }
                                    p.AddExplosiveDamage(blastDamage, 100, ExplosionSourceType.Missile);

                                    // Scoring
                                    var aName = Sourcevessel;       // Attacker
                                    var tName = p.vessel.GetName(); // Target
                                    if (tName != null && aName != tName && BDACompetitionMode.Instance.Scores.ContainsKey(tName) && BDACompetitionMode.Instance.Scores.ContainsKey(aName))
                                    {
                                        // Part hit counts
                                        if (BDACompetitionMode.Instance.Scores[tName].missilePartDamageCounts.ContainsKey(aName))
                                        {
                                            ++BDACompetitionMode.Instance.Scores[tName].missilePartDamageCounts[aName];
                                        }
                                        else
                                        {
                                            BDACompetitionMode.Instance.Scores[tName].missilePartDamageCounts[aName] = 1;
                                        }
                                        if (!BDACompetitionMode.Instance.Scores[tName].everyoneWhoHitMeWithMissiles.Contains(aName))
                                        {
                                            BDACompetitionMode.Instance.Scores[tName].everyoneWhoHitMeWithMissiles.Add(aName);
                                        }
                                        ++BDACompetitionMode.Instance.Scores[aName].totalDamagedPartsDueToMissiles;
                                        BDACompetitionMode.Instance.Scores[tName].lastMissileHitTime             = Planetarium.GetUniversalTime();
                                        BDACompetitionMode.Instance.Scores[tName].lastPersonWhoHitMeWithAMissile = aName;
                                        if (vesselsHitByMissiles.ContainsKey(tName))
                                        {
                                            ++vesselsHitByMissiles[tName];
                                        }
                                        else
                                        {
                                            vesselsHitByMissiles[tName] = 1;
                                        }
                                        if (BDArmorySettings.REMOTE_LOGGING_ENABLED)
                                        {
                                            BDAScoreService.Instance.TrackMissileParts(aName, tName, 1);
                                        }

                                        // Part damage scoring
                                        var tData = BDACompetitionMode.Instance.Scores[tName];
                                        if (tData.damageFromMissiles.ContainsKey(aName))
                                        {
                                            tData.damageFromMissiles[aName] += blastDamage;
                                        }
                                        else
                                        {
                                            tData.damageFromMissiles.Add(aName, blastDamage);
                                        }
                                        if (BDArmorySettings.REMOTE_LOGGING_ENABLED)
                                        {
                                            BDAScoreService.Instance.TrackMissileDamage(aName, tName, blastDamage);
                                        }
                                        if (BDArmorySettings.DRAW_DEBUG_LABELS)
                                        {
                                            Debug.Log("[BDArmory.NukeTest]: " + aName + " did " + blastDamage + " blast damage to " + tName + " at " + distToG0.ToString("0.000") + "m");
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        DestructibleBuilding building = blastHits.Current.GetComponentInParent <DestructibleBuilding>();

                        if (building != null)
                        {
                            var distToEpicenter = Mathf.Max((part.transform.position - building.transform.position).magnitude, 1f);
                            var blastImpulse    = Mathf.Pow(3.01f * 1100f / distToEpicenter, 1.25f) * 6.894f * lastValidAtmDensity * yieldCubeRoot;
                            // blastImpulse = (((((Math.Pow((Math.Pow((Math.Pow((9.54 * Math.Pow(10.0, -3.0) * (2200.0 / distToEpicenter)), 1.95)), 4.0) + Math.Pow((Math.Pow((3.01 * (1100.0 / distToEpicenter)), 1.25)), 4.0)), 0.25)) * 6.894) * (vessel.atmDensity)) * Math.Pow(yield, (1.0 / 3.0))));
                            if (!double.IsNaN(blastImpulse) && blastImpulse > 140) //140kPa, level at which reinforced concrete structures are destroyed
                            {
                                building.Demolish();
                            }
                        }
                    }
                }
            }
            if (vesselsHitByMissiles.Count > 0)
            {
                string message = "";
                foreach (var vesselName in vesselsHitByMissiles.Keys)
                {
                    message += (message == "" ? "" : " and ") + vesselName + " had " + vesselsHitByMissiles[vesselName];
                }
                message += " parts damaged (Blast Wave) by " + Sourcevessel + "'s exploding engine core.";
                BDACompetitionMode.Instance.competitionStatus.Add(message);
                Debug.Log("[BDArmory.NukeTest]: " + message);
            }
            ExplosionFx.CreateExplosion(part.transform.position, 1, explModelPath, explSoundPath, ExplosionSourceType.Missile, 0, null, Sourcevessel, "Reactor Containment Failure");
            hasDetonated = true;
            if (part.vessel != null) // Already in the process of being destroyed.
            {
                part.Destroy();
            }
        }
Exemple #5
0
        void Detonate()
        {
            if (!parentPart.partName.Contains("exploding"))
            {
                bool excessFuel = false;
                parentPart.partName += "exploding";
                PartResource fuel = parentPart.Resources.Where(pr => pr.resourceName == "LiquidFuel").FirstOrDefault();
                PartResource ox   = parentPart.Resources.Where(pr => pr.resourceName == "Oxidizer").FirstOrDefault();
                if (fuel != null)
                {
                    tntMassEquivilent += (Mathf.Clamp((float)fuel.amount, ((float)fuel.maxAmount * 0.05f), ((float)fuel.maxAmount * 0.2f)) / 2);
                    if (fuel != null && ox != null)
                    {
                        tntMassEquivilent += (Mathf.Clamp((float)ox.amount, ((float)ox.maxAmount * 0.1f), ((float)ox.maxAmount * 0.3f)) / 2);
                        tntMassEquivilent *= 1.3f;
                    }
                    if (fuel.amount > fuel.maxAmount * 0.3f)
                    {
                        excessFuel = true;
                    }
                }
                PartResource mp = parentPart.Resources.Where(pr => pr.resourceName == "MonoPropellant").FirstOrDefault();
                if (mp != null)
                {
                    tntMassEquivilent += (Mathf.Clamp((float)mp.amount, ((float)mp.maxAmount * 0.1f), ((float)mp.maxAmount * 0.3f)) / 3);
                    if (mp.amount > mp.maxAmount * 0.3f)
                    {
                        excessFuel = true;
                    }
                }
                PartResource ec = parentPart.Resources.Where(pr => pr.resourceName == "ElectricCharge").FirstOrDefault();
                if (ec != null)
                {
                    tntMassEquivilent += ((float)ec.maxAmount / 5000); //fix for cockpit batteries weighing a tonne+
                    ec.maxAmount       = 0;
                    ec.isVisible       = false;
                    parentPart.RemoveResource(ec);//destroy battery. not calling part.destroy, since some batteries in cockpits.
                    Misc.Misc.RefreshAssociatedWindows(parentPart);
                }
                tntMassEquivilent *= BDArmorySettings.BD_AMMO_DMG_MULT;
                if (BDArmorySettings.DRAW_DEBUG_LABELS)
                {
                    Debug.Log("[BDArmory.FireFX] Fuel Explosion in " + this.parentPart.name + ", TNT mass equivilent " + tntMassEquivilent);
                }
                if (excessFuel)
                {
                    float blastRadius = BlastPhysicsUtils.CalculateBlastRange(tntMassEquivilent);
                    using (var blastHits = Physics.OverlapSphere(parentPart.transform.position, blastRadius, 9076737).AsEnumerable().GetEnumerator())
                    {
                        while (blastHits.MoveNext())
                        {
                            if (blastHits.Current == null)
                            {
                                continue;
                            }
                            try
                            {
                                Part partHit = blastHits.Current.GetComponentInParent <Part>();
                                if (partHit == null)
                                {
                                    continue;
                                }
                                if (ProjectileUtils.IsIgnoredPart(partHit))
                                {
                                    continue;                                         // Ignore ignored parts.
                                }
                                if (partHit != null && partHit.mass > 0)
                                {
                                    Rigidbody rb       = partHit.Rigidbody;
                                    Vector3   distToG0 = parentPart.transform.position - partHit.transform.position;

                                    Ray        LoSRay = new Ray(parentPart.transform.position, partHit.transform.position - parentPart.transform.position);
                                    RaycastHit hit;
                                    if (Physics.Raycast(LoSRay, out hit, distToG0.magnitude, 9076737))
                                    {
                                        KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards <KerbalEVA>();
                                        Part      p   = eva ? eva.part : hit.collider.gameObject.GetComponentInParent <Part>();
                                        if (p == partHit)
                                        {
                                            if (rb == null)
                                            {
                                                return;
                                            }
                                            BulletHitFX.AttachFire(hit, p, 1, SourceVessel, BDArmorySettings.WEAPON_FX_DURATION * (1 - (distToG0.magnitude / blastRadius)));
                                            if (BDArmorySettings.DRAW_DEBUG_LABELS)
                                            {
                                                Debug.Log("[BDArmory.FireFX] " + this.parentPart.name + " hit by burning fuel");
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                Debug.LogWarning("[BDArmory.FireFX]: Exception thrown in Detonate: " + e.Message + "\n" + e.StackTrace);
                            }
                        }
                    }
                }
                ExplosionFx.CreateExplosion(parentPart.transform.position, tntMassEquivilent, explModelPath, explSoundPath, ExplosionSourceType.Bullet, 0, null, parentPart.vessel != null ? parentPart.vessel.name : null, null);
                // needs to be Explosiontype Bullet since missile only returns Module MissileLauncher
                gameObject.SetActive(false);
            }
        }
        /// <summary>
        /// Check for bullet collision in the upcoming period.
        /// </summary>
        /// <param name="period">Period to consider, typically Time.fixedDeltaTime</param>
        /// <param name="reverse">Also perform raycast in reverse to detect collisions from rays starting within an object.</param>
        /// <returns>true if a collision is detected, false otherwise.</returns>
        public bool CheckBulletCollision(float period, bool reverse = false)
        {
            //reset our hit variables to default state
            hasPenetrated = true;
            hasDetonated  = false;
            hasRicocheted = false;
            penTicker     = 0;
            currPosition  = transform.position;

            float dist = currentVelocity.magnitude * period;

            bulletRay = new Ray(currPosition, currentVelocity + 0.5f * period * FlightGlobals.getGeeForceAtPosition(transform.position));
            var hitCount = Physics.RaycastNonAlloc(bulletRay, hits, dist, 9076737);

            if (hitCount == hits.Length) // If there's a whole bunch of stuff in the way (unlikely), then we need to increase the size of our hits buffer.
            {
                hits     = Physics.RaycastAll(bulletRay, dist, 9076737);
                hitCount = hits.Length;
            }
            int reverseHitCount = 0;

            if (reverse)
            {
                reverseHitCount = Physics.RaycastNonAlloc(new Ray(currPosition + currentVelocity * period, -currentVelocity), reverseHits, dist, 9076737);
                if (reverseHitCount == reverseHits.Length)
                {
                    reverseHits     = Physics.RaycastAll(new Ray(currPosition + currentVelocity * period, -currentVelocity), dist, 9076737);
                    reverseHitCount = reverseHits.Length;
                }
                for (int i = 0; i < reverseHitCount; ++i)
                {
                    reverseHits[i].distance = dist - reverseHits[i].distance;
                }
            }
            if (hitCount + reverseHitCount > 0)
            {
                var orderedHits = hits.Take(hitCount).Concat(reverseHits.Take(reverseHitCount)).OrderBy(x => x.distance);

                using (var hitsEnu = orderedHits.GetEnumerator())
                {
                    RaycastHit hit;
                    Part       hitPart;
                    KerbalEVA  hitEVA;

                    while (hitsEnu.MoveNext())
                    {
                        if (!hasPenetrated || hasRicocheted || hasDetonated)
                        {
                            return(true);
                        }

                        hit     = hitsEnu.Current;
                        hitPart = null;
                        hitEVA  = null;

                        try
                        {
                            hitPart = hit.collider.gameObject.GetComponentInParent <Part>();
                            hitEVA  = hit.collider.gameObject.GetComponentUpwards <KerbalEVA>();
                        }
                        catch (NullReferenceException e)
                        {
                            Debug.Log("[BDArmory.PooledBullet]:NullReferenceException for Ballistic Hit: " + e.Message);
                            return(true);
                        }

                        if (hitPart != null && ProjectileUtils.IsIgnoredPart(hitPart))
                        {
                            continue;                                                            // Ignore ignored parts.
                        }
                        if (hitEVA != null)
                        {
                            hitPart = hitEVA.part;
                            // relative velocity, separate from the below statement, because the hitpart might be assigned only above
                            if (hitPart.rb != null)
                            {
                                impactVelocity = (currentVelocity * dragVelocityFactor - (hitPart.rb.velocity + Krakensbane.GetFrameVelocityV3f())).magnitude;
                            }
                            else
                            {
                                impactVelocity = currentVelocity.magnitude * dragVelocityFactor;
                            }
                            distanceTraveled += hit.distance;
                            ProjectileUtils.ApplyDamage(hitPart, hit, 1, 1, caliber, bulletMass, impactVelocity, bulletDmgMult, distanceTraveled, explosive, incendiary, hasRicocheted, sourceVessel, bullet.name, team);
                            ExplosiveDetonation(hitPart, hit, bulletRay);
                            KillBullet(); // Kerbals are too thick-headed for penetration...
                            return(true);
                        }

                        if (hitPart != null && hitPart.vessel == sourceVessel)
                        {
                            continue;                                                     //avoid autohit;
                        }
                        Vector3 impactVector = currentVelocity;
                        if (hitPart != null && hitPart.rb != null)
                        {
                            // using relative velocity vector instead of just bullet velocity
                            // since KSP vessels might move faster than bullets
                            impactVector = currentVelocity * dragVelocityFactor - (hitPart.rb.velocity + Krakensbane.GetFrameVelocityV3f());
                        }

                        float hitAngle = Vector3.Angle(impactVector, -hit.normal);

                        if (ProjectileUtils.CheckGroundHit(hitPart, hit, caliber))
                        {
                            ProjectileUtils.CheckBuildingHit(hit, bulletMass, currentVelocity, bulletDmgMult);
                            if (!RicochetScenery(hitAngle))
                            {
                                ExplosiveDetonation(hitPart, hit, bulletRay);
                                KillBullet();
                                distanceTraveled += hit.distance;
                                return(true);
                            }
                            else
                            {
                                DoRicochet(hitPart, hit, hitAngle, hit.distance / dist, period);
                                return(true);
                            }
                        }

                        //Standard Pipeline Hitpoints, Armor and Explosives
                        impactVelocity = impactVector.magnitude;
                        if (massMod != 0)
                        {
                            var ME = hitPart.FindModuleImplementing <ModuleMassAdjust>();
                            if (ME == null)
                            {
                                ME = (ModuleMassAdjust)hitPart.AddModule("ModuleMassAdjust");
                            }
                            ME.massMod  += massMod;
                            ME.duration += BDArmorySettings.WEAPON_FX_DURATION;
                        }
                        if (impulse != 0 && hitPart.rb != null)
                        {
                            hitPart.rb.AddForceAtPosition(impactVector.normalized * impulse, hit.point, ForceMode.Acceleration);
                            ProjectileUtils.ApplyScore(hitPart, sourceVessel, distanceTraveled, 0, bullet.name);
                            break; //impulse rounds shouldn't penetrate/do damage
                        }
                        float anglemultiplier = (float)Math.Cos(Math.PI * hitAngle / 180.0);

                        float thickness         = ProjectileUtils.CalculateThickness(hitPart, anglemultiplier);
                        float penetration       = ProjectileUtils.CalculatePenetration(caliber, bulletMass, impactVelocity, apBulletMod);
                        float penetrationFactor = ProjectileUtils.CalculateArmorPenetration(hitPart, anglemultiplier, hit, penetration, thickness, caliber);
                        if (penetration > thickness)
                        {
                            currentVelocity = currentVelocity * (float)Math.Sqrt(thickness / penetration);
                            if (penTicker > 0)
                            {
                                currentVelocity *= 0.55f;
                            }
                            flightTimeElapsed -= period;
                        }
                        if (penetrationFactor >= 2)
                        {
                            //its not going to bounce if it goes right through
                            hasRicocheted = false;
                        }
                        else
                        {
                            if (RicochetOnPart(hitPart, hit, hitAngle, impactVelocity, hit.distance / dist, period))
                            {
                                hasRicocheted = true;
                            }
                        }

                        if (penetrationFactor > 1 && !hasRicocheted) //fully penetrated continue ballistic damage
                        {
                            hasPenetrated = true;
                            ProjectileUtils.ApplyDamage(hitPart, hit, 1, penetrationFactor, caliber, bulletMass, impactVelocity, bulletDmgMult, distanceTraveled, explosive, incendiary, hasRicocheted, sourceVessel, bullet.name, team);
                            penTicker += 1;
                            ProjectileUtils.CheckPartForExplosion(hitPart);

                            //Explosive bullets that penetrate should explode shortly after
                            //if penetration is very great, they will have moved on
                            //checking velocity as they would not be able to come out the other side
                            //if (explosive && penetrationFactor < 3 || currentVelocity.magnitude <= 800f)
                            if (explosive)
                            {
                                //move bullet
                                transform.position += (currentVelocity * period) / 3;

                                distanceTraveled += hit.distance;
                                ExplosiveDetonation(hitPart, hit, bulletRay);
                                hasDetonated = true;
                                KillBullet();
                                return(true);
                            }
                        }
                        else if (!hasRicocheted) // explosive bullets that get stopped by armor will explode
                        {
                            if (hitPart.rb != null && hitPart.rb.mass > 0)
                            {
                                float forceAverageMagnitude = impactVelocity * impactVelocity *
                                                              (1f / hit.distance) * (bulletMass - tntMass);

                                float accelerationMagnitude =
                                    forceAverageMagnitude / (hitPart.vessel.GetTotalMass() * 1000);

                                hitPart.rb.AddForceAtPosition(impactVector.normalized * accelerationMagnitude, hit.point, ForceMode.Acceleration);

                                if (BDArmorySettings.DRAW_DEBUG_LABELS)
                                {
                                    Debug.Log("[BDArmory.PooledBullet]: Force Applied " + Math.Round(accelerationMagnitude, 2) + "| Vessel mass in kgs=" + hitPart.vessel.GetTotalMass() * 1000 + "| bullet effective mass =" + (bulletMass - tntMass));
                                }
                            }

                            distanceTraveled += hit.distance;
                            hasPenetrated     = false;
                            ProjectileUtils.ApplyDamage(hitPart, hit, 1, penetrationFactor, caliber, bulletMass, impactVelocity, bulletDmgMult, distanceTraveled, explosive, incendiary, hasRicocheted, sourceVessel, bullet.name, team);
                            ExplosiveDetonation(hitPart, hit, bulletRay);
                            hasDetonated = true;
                            KillBullet();
                            return(true);
                        }

                        /////////////////////////////////////////////////////////////////////////////////
                        // penetrated after a few ticks
                        /////////////////////////////////////////////////////////////////////////////////

                        //penetrating explosive
                        //richochets
                        if ((penTicker >= 2 && explosive) || (hasRicocheted && explosive))
                        {
                            //detonate
                            ExplosiveDetonation(hitPart, hit, bulletRay, airDetonation);
                            distanceTraveled += hit.distance;
                            return(true);
                        }

                        //bullet should not go any further if moving too slowly after hit
                        //smaller caliber rounds would be too deformed to do any further damage
                        if (currentVelocity.magnitude <= 100 && hasPenetrated)
                        {
                            if (BDArmorySettings.DRAW_DEBUG_LABELS)
                            {
                                Debug.Log("[BDArmory.PooledBullet]: Bullet Velocity too low, stopping");
                            }
                            KillBullet();
                            distanceTraveled += hit.distance;
                            return(true);
                        }
                    } //end While
                }     //end enumerator
            }         //end of hits
            return(false);
        }
        public void DetonateIfPossible()
        {
            if (!hasDetonated)
            {
                hasDetonated = true; // Set hasDetonated here to avoid recursive calls due to ammo boxes exploding each other.
                var     vesselName = vessel != null ? vessel.vesselName : null;
                Vector3 direction  = default(Vector3);
                GetBlastRadius();
                if (CASELevel == 0) //a considerable quantity of explosives and propellants just detonated inside your ship
                {
                    ExplosionFx.CreateExplosion(part.transform.position, (float)ammoExplosionYield * BDArmorySettings.BD_AMMO_DMG_MULT, explModelPath, explSoundPath, ExplosionSourceType.Missile, 0, part, vesselName, null, direction);
                    if (BDArmorySettings.DRAW_DEBUG_LABELS)
                    {
                        Debug.Log("[BDArmory.ModuleCASE] CASE 0 explosion, tntMassEquivilent: " + ammoExplosionYield);
                    }
                }
                else if (CASELevel == 1) // the blast is reduced. Damage is severe, but (potentially) survivable
                {
                    ExplosionFx.CreateExplosion(part.transform.position, ((float)ammoExplosionYield / 2), limitEdexploModelPath, explSoundPath, ExplosionSourceType.Missile, 0, part, vesselName, null, direction, true);
                    if (BDArmorySettings.DRAW_DEBUG_LABELS)
                    {
                        Debug.Log("[BDArmory.ModuleCASE] CASE I explosion, tntMassEquivilent: " + ammoExplosionYield + ", part: " + part + ", vessel: " + vesselName);
                    }
                    using (var blastHits = Physics.OverlapSphere(part.transform.position, blastRadius / 2, 9076737).AsEnumerable().GetEnumerator())
                    {
                        while (blastHits.MoveNext())
                        {
                            if (blastHits.Current == null)
                            {
                                continue;
                            }
                            try
                            {
                                Part partHit = blastHits.Current.GetComponentInParent <Part>();
                                if (partHit == null || partHit == part)
                                {
                                    continue;
                                }
                                if (ProjectileUtils.IsIgnoredPart(partHit))
                                {
                                    continue;                                         // Ignore ignored parts.
                                }
                                if (partHit.mass > 0)
                                {
                                    Rigidbody rb = partHit.Rigidbody;
                                    if (rb == null)
                                    {
                                        continue;
                                    }
                                    Vector3 distToG0 = part.transform.position - partHit.transform.position;

                                    Ray        LoSRay = new Ray(part.transform.position, partHit.transform.position - part.transform.position);
                                    RaycastHit hit;
                                    if (Physics.Raycast(LoSRay, out hit, distToG0.magnitude, 9076737))
                                    {
                                        if (FlightGlobals.currentMainBody == null || hit.collider.gameObject != FlightGlobals.currentMainBody.gameObject)
                                        {
                                            KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards <KerbalEVA>();
                                            Part      p   = eva ? eva.part : hit.collider.gameObject.GetComponentInParent <Part>();
                                            if (p == partHit)
                                            {
                                                ApplyDamage(p, hit, (1 - Mathf.Sqrt(distToG0.magnitude / (blastRadius / 2))));
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                Debug.LogError("[BDArmory.ModuleCASE]: Exception in AmmoExplosion Hit: " + e.Message + "\n" + e.StackTrace);
                            }
                        }
                    }
                }
                else //if (CASELevel == 2) //blast contained, shunted out side of hull, minimal damage
                {
                    ExplosionFx.CreateExplosion(part.transform.position, (float)ammoExplosionYield, shuntExploModelPath, explSoundPath, ExplosionSourceType.Missile, 0, part, vesselName, null, direction, true);
                    if (BDArmorySettings.DRAW_DEBUG_LABELS)
                    {
                        Debug.Log("[BDArmory.ModuleCASE] CASE II explosion, tntMassEquivilent: " + ammoExplosionYield);
                    }
                    Ray BlastRay = new Ray(part.transform.position, part.transform.up);
                    var hits     = Physics.RaycastAll(BlastRay, blastRadius, 9076737);
                    if (hits.Length > 0)
                    {
                        var orderedHits = hits.OrderBy(x => x.distance);

                        using (var hitsEnu = orderedHits.GetEnumerator())
                        {
                            while (hitsEnu.MoveNext())
                            {
                                RaycastHit hit     = hitsEnu.Current;
                                Part       hitPart = null;
                                KerbalEVA  hitEVA  = null;

                                if (hit.collider.gameObject != FlightGlobals.currentMainBody.gameObject)
                                {
                                    try
                                    {
                                        hitPart = hit.collider.gameObject.GetComponentInParent <Part>();
                                        hitEVA  = hit.collider.gameObject.GetComponentUpwards <KerbalEVA>();
                                    }
                                    catch (NullReferenceException e)
                                    {
                                        Debug.LogError("[BDArmory.ModuleCASE]: NullReferenceException for AmmoExplosion Hit: " + e.Message + "\n" + e.StackTrace);
                                        continue;
                                    }

                                    if (hitPart == null || hitPart == part)
                                    {
                                        continue;
                                    }
                                    if (ProjectileUtils.IsIgnoredPart(hitPart))
                                    {
                                        continue;                                         // Ignore ignored parts.
                                    }
                                    if (hitEVA != null)
                                    {
                                        hitPart = hitEVA.part;
                                        if (hitPart.rb != null)
                                        {
                                            ApplyDamage(hitPart, hit);
                                        }
                                        break;
                                    }
                                    ApplyDamage(hitPart, hit);
                                }
                            }
                        }
                    }
                }
                if (part.vessel != null) // Already in the process of being destroyed.
                {
                    part.Destroy();
                }
            }
        }
        void Detonate(Vector3 pos, bool missed)
        {
            if (!missed)
            {
                if (tntMass > 0)
                {
                    Vector3 direction = default(Vector3);
                    if (shaped)
                    {
                        direction = (pos + rb.velocity * Time.deltaTime).normalized;
                    }
                    if (gravitic)
                    {
                        using (var hitsEnu = Physics.OverlapSphere(transform.position, blastRadius, 557057).AsEnumerable().GetEnumerator())
                        {
                            while (hitsEnu.MoveNext())
                            {
                                if (hitsEnu.Current == null)
                                {
                                    continue;
                                }

                                Part partHit = hitsEnu.Current.GetComponentInParent <Part>();
                                if (partHit == null)
                                {
                                    continue;
                                }
                                if (ProjectileUtils.IsIgnoredPart(partHit))
                                {
                                    continue;                                         // Ignore ignored parts.
                                }
                                float distance = Vector3.Distance(transform.position, partHit.transform.position);
                                if (gravitic)
                                {
                                    if (partHit.mass > 0)
                                    {
                                        var ME = partHit.vessel.rootPart.FindModuleImplementing <ModuleMassAdjust>();
                                        if (ME == null)
                                        {
                                            ME = (ModuleMassAdjust)partHit.vessel.rootPart.AddModule("ModuleMassAdjust");
                                        }
                                        ME.massMod  += (massMod * (1 - (distance / blastRadius)));                             //this way craft at edge of blast might only get disabled instead of bricked
                                        ME.duration += (BDArmorySettings.WEAPON_FX_DURATION * (1 - (distance / blastRadius))); //can bypass EMP damage cap
                                    }
                                }
                            }
                        }
                    }
                    if (incendiary)
                    {
                        for (int f = 0; f < 20; f++) //throw 20 random raytraces out in a sphere and see what gets tagged
                        {
                            Ray        LoSRay = new Ray(transform.position, VectorUtils.GaussianDirectionDeviation(transform.forward, 170));
                            RaycastHit hit;
                            if (Physics.Raycast(LoSRay, out hit, blastRadius * 1.2f, 9076737)) // only add fires to parts in LoS of blast
                            {
                                KerbalEVA eva      = hit.collider.gameObject.GetComponentUpwards <KerbalEVA>();
                                Part      p        = eva ? eva.part : hit.collider.gameObject.GetComponentInParent <Part>();
                                float     distance = Vector3.Distance(transform.position, hit.point);
                                if (p != null)
                                {
                                    BulletHitFX.AttachFire(hit, p, caliber, sourceVesselName, BDArmorySettings.WEAPON_FX_DURATION * (1 - (distance / blastRadius)), 1, false, true); //else apply fire to occluding part
                                    if (BDArmorySettings.DRAW_DEBUG_LABELS)
                                    {
                                        Debug.Log("[BDArmory.Rocket]: Applying fire to " + p.name + " at distance " + distance + "m, for " + BDArmorySettings.WEAPON_FX_DURATION * (1 - (distance / blastRadius)) + " seconds");
                                    }
                                    ;
                                }
                            }
                            if (BDArmorySettings.DRAW_DEBUG_LABELS)
                            {
                                Debug.Log("[Rocket] incendiary raytrace: " + hit.point.x + "; " + hit.point.y + "; " + hit.point.z);
                            }
                        }
                    }
                    if (concussion || EMP || choker)
                    {
                        using (var hitsEnu = Physics.OverlapSphere(transform.position, 25, 557057).AsEnumerable().GetEnumerator())
                        {
                            var craftHit = new HashSet <Vessel>();
                            while (hitsEnu.MoveNext())
                            {
                                if (hitsEnu.Current == null)
                                {
                                    continue;
                                }
                                if (hitsEnu.Current.gameObject == FlightGlobals.currentMainBody.gameObject)
                                {
                                    continue;                                                                         // Ignore terrain hits.
                                }
                                Part partHit = hitsEnu.Current.GetComponentInParent <Part>();
                                if (partHit == null)
                                {
                                    continue;
                                }
                                if (ProjectileUtils.IsIgnoredPart(partHit))
                                {
                                    continue;                                         // Ignore ignored parts.
                                }
                                if (craftHit.Contains(partHit.vessel))
                                {
                                    continue;                                    // Don't hit the same craft multiple times.
                                }
                                craftHit.Add(partHit.vessel);

                                float Distance = Vector3.Distance(partHit.transform.position, this.transform.position);
                                if (partHit != null)
                                {
                                    if (concussion && partHit.mass > 0)
                                    {
                                        partHit.rb.AddForceAtPosition((partHit.transform.position - this.transform.position).normalized * impulse, partHit.transform.position, ForceMode.Acceleration);
                                    }
                                    if (EMP)
                                    {
                                        var MDEC = partHit.vessel.rootPart.FindModuleImplementing <ModuleDrainEC>();
                                        if (MDEC == null)
                                        {
                                            MDEC = (ModuleDrainEC)partHit.vessel.rootPart.AddModule("ModuleDrainEC");
                                        }
                                        MDEC.incomingDamage += ((25 - Distance) * 5); //this way craft at edge of blast might only get disabled instead of bricked
                                        MDEC.softEMP         = false;                 //can bypass EMP damage cap
                                    }
                                    if (choker)
                                    {
                                        var ash = partHit.vessel.rootPart.FindModuleImplementing <ModuleDrainIntakes>();
                                        if (ash == null)
                                        {
                                            ash = (ModuleDrainIntakes)partHit.vessel.rootPart.AddModule("ModuleDrainIntakes");
                                        }
                                        ash.drainDuration += BDArmorySettings.WEAPON_FX_DURATION * (1 - (Distance / 25)); //reduce intake knockout time based on distance from epicenter
                                    }
                                }
                            }
                        }
                        ExplosionFx.CreateExplosion(pos, tntMass, explModelPath, explSoundPath, ExplosionSourceType.Bullet, caliber, null, sourceVesselName, null, direction, true);
                    }
                    else
                    {
                        ExplosionFx.CreateExplosion(pos, tntMass, explModelPath, explSoundPath, ExplosionSourceType.Bullet, caliber, null, sourceVesselName, null, direction);
                    }
                }
            } // needs to be Explosiontype Bullet since missile only returns Module MissileLauncher
            gameObject.SetActive(false);
        }
        private bool ProximityAirDetonation(float distanceFromStart)
        {
            bool detonate = false;

            if (distanceFromStart <= blastRadius)
            {
                return(false);
            }

            if (!explosive || tntMass <= 0)
            {
                return(false);
            }

            if (flak)
            {
                using (var hitsEnu = Physics.OverlapSphere(transform.position, detonationRange, 557057).AsEnumerable().GetEnumerator())
                {
                    while (hitsEnu.MoveNext())
                    {
                        if (hitsEnu.Current == null)
                        {
                            continue;
                        }
                        try
                        {
                            Part partHit = hitsEnu.Current.GetComponentInParent <Part>();
                            if (partHit == null)
                            {
                                continue;
                            }
                            if (partHit.vessel == sourceVessel)
                            {
                                continue;
                            }
                            if (ProjectileUtils.IsIgnoredPart(partHit))
                            {
                                continue;                       // Ignore ignored parts.
                            }
                            var aName = sourceVessel.GetName(); //proxi detonated rocket scoring
                            var tName = partHit.vessel.GetName();

                            if (aName != null && tName != null && aName != tName && BDACompetitionMode.Instance.Scores.ContainsKey(aName) && BDACompetitionMode.Instance.Scores.ContainsKey(tName))
                            {
                                if (BDArmorySettings.REMOTE_LOGGING_ENABLED)
                                {
                                    BDAScoreService.Instance.TrackHit(aName, tName, name, distanceFromStart);
                                }
                                var aData = BDACompetitionMode.Instance.Scores[aName];
                                aData.Score += 1;
                                if (partHit.vessel.GetName() == "Pinata")
                                {
                                    aData.PinataHits++;
                                }

                                var tData = BDACompetitionMode.Instance.Scores[tName];
                                tData.lastPersonWhoHitMe = aName;
                                tData.lastHitTime        = Planetarium.GetUniversalTime();
                                tData.everyoneWhoHitMe.Add(aName);

                                if (tData.hitCounts.ContainsKey(aName))
                                {
                                    ++tData.hitCounts[aName];
                                }
                                else
                                {
                                    tData.hitCounts.Add(aName, 1);
                                }
                            }
                            if (BDArmorySettings.DRAW_DEBUG_LABELS)
                            {
                                Debug.Log("[BDArmory.PooledRocket]: rocket proximity sphere hit | Distance overlap = " + detonationRange + "| Part name = " + partHit.name);
                            }
                            return(detonate = true);
                        }
                        catch (Exception e)
                        {
                            Debug.LogWarning("[BDArmory.PooledRocket]: Exception thrown in ProximityAirDetonation: " + e.Message + "\n" + e.StackTrace);
                        }
                    }
                }
            }
            return(detonate);
        }
        void FixedUpdate()
        {
            if (!gameObject.activeInHierarchy)
            {
                return;
            }
            //floating origin and velocity offloading corrections
            if (!FloatingOrigin.Offset.IsZero() || !Krakensbane.GetFrameVelocity().IsZero())
            {
                transform.position -= FloatingOrigin.OffsetNonKrakensbane;
                prevPosition       -= FloatingOrigin.OffsetNonKrakensbane;
                startPosition      -= FloatingOrigin.OffsetNonKrakensbane;
            }
            distanceFromStart = Vector3.Distance(transform.position, startPosition);

            if (Time.time - startTime < stayTime && transform.parent != null)
            {
                transform.rotation = transform.parent.rotation;
                transform.position = spawnTransform.position;
                //+(transform.parent.rigidbody.velocity*Time.fixedDeltaTime);
            }
            else
            {
                if (transform.parent != null && parentRB)
                {
                    transform.parent = null;
                    rb.isKinematic   = false;
                    rb.velocity      = parentRB.velocity + Krakensbane.GetFrameVelocityV3f();
                }
            }

            if (rb && !rb.isKinematic)
            {
                //physics
                if (FlightGlobals.RefFrameIsRotating)
                {
                    rb.velocity += FlightGlobals.getGeeForceAtPosition(transform.position) * Time.fixedDeltaTime;
                }

                //guidance and attitude stabilisation scales to atmospheric density.
                float atmosMultiplier =
                    Mathf.Clamp01(2.5f *
                                  (float)
                                  FlightGlobals.getAtmDensity(FlightGlobals.getStaticPressure(transform.position),
                                                              FlightGlobals.getExternalTemperature(), FlightGlobals.currentMainBody));

                //model transform. always points prograde
                transform.rotation = Quaternion.RotateTowards(transform.rotation,
                                                              Quaternion.LookRotation(rb.velocity + Krakensbane.GetFrameVelocity(), transform.up),
                                                              atmosMultiplier * (0.5f * (Time.time - startTime)) * 50 * Time.fixedDeltaTime);


                if (Time.time - startTime < thrustTime && Time.time - startTime > stayTime)
                {
                    thrustVector.x = randomThrustDeviation * (1 - (Mathf.PerlinNoise(4 * Time.time, randThrustSeed) * 2)) / massScalar; //this needs to scale w/ rocket mass, or light projectiles will be
                    thrustVector.y = randomThrustDeviation * (1 - (Mathf.PerlinNoise(randThrustSeed, 4 * Time.time) * 2)) / massScalar; //far more affected than heavier ones
                    rb.AddRelativeForce(thrustVector);
                }//0.012/rocketmass - use .012 as baseline, it's the mass of the hydra, which the randomTurstdeviation was originally calibrated for
            }

            if (Time.time - startTime > thrustTime)
            {
                using (var pe = pEmitters.AsEnumerable().GetEnumerator())
                    while (pe.MoveNext())
                    {
                        if (pe.Current == null)
                        {
                            continue;
                        }
                        pe.Current.emit = false;
                    }
                using (var gpe = gpEmitters.AsEnumerable().GetEnumerator())
                    while (gpe.MoveNext())
                    {
                        if (gpe.Current == null)
                        {
                            continue;
                        }
                        gpe.Current.emit = false;
                    }
                if (audioSource)
                {
                    audioSource.loop = false;
                    audioSource.Stop();
                }
            }
            if (Time.time - startTime > 0.1f + stayTime)
            {
                hasPenetrated = true;
                hasDetonated  = false;
                penTicker     = 0;

                currPosition = transform.position;
                float dist = (currPosition - prevPosition).magnitude;
                RocketRay = new Ray(prevPosition, currPosition - prevPosition);
                var hits = Physics.RaycastAll(RocketRay, dist, 9076737);
                if (hits.Length > 0)
                {
                    var orderedHits = hits.OrderBy(x => x.distance);

                    using (var hitsEnu = orderedHits.GetEnumerator())
                    {
                        while (hitsEnu.MoveNext())
                        {
                            if (!hasPenetrated || hasDetonated)
                            {
                                break;
                            }

                            RaycastHit hit     = hitsEnu.Current;
                            Part       hitPart = null;
                            KerbalEVA  hitEVA  = null;

                            try
                            {
                                hitPart = hit.collider.gameObject.GetComponentInParent <Part>();
                                hitEVA  = hit.collider.gameObject.GetComponentUpwards <KerbalEVA>();
                            }
                            catch (NullReferenceException e)
                            {
                                Debug.LogWarning("[BDArmory.BDArmory]:NullReferenceException for Kinetic Hit: " + e.Message);
                                return;
                            }

                            if (hitPart != null && ProjectileUtils.IsIgnoredPart(hitPart))
                            {
                                continue;                                                            // Ignore ignored parts.
                            }
                            if (hitEVA != null)
                            {
                                hitPart = hitEVA.part;
                                // relative velocity, separate from the below statement, because the hitpart might be assigned only above
                                if (hitPart.rb != null)
                                {
                                    impactVelocity = (rb.velocity - (hitPart.rb.velocity + Krakensbane.GetFrameVelocityV3f())).magnitude;
                                }
                                else
                                {
                                    impactVelocity = rb.velocity.magnitude;
                                }
                                ProjectileUtils.ApplyDamage(hitPart, hit, 1, 1, caliber, rocketMass, impactVelocity, bulletDmgMult, distanceFromStart, explosive, incendiary, false, sourceVessel, rocketName, team);
                                Detonate(hit.point, false);
                                return;
                            }

                            if (hitPart != null && hitPart.vessel == sourceVessel)
                            {
                                continue;                                                     //avoid autohit;
                            }
                            Vector3 impactVector = rb.velocity;
                            if (hitPart != null && hitPart.rb != null)
                            {
                                // using relative velocity vector instead of just rocket velocity
                                // since KSP vessels can easily be moving faster than rockets
                                impactVector = rb.velocity - (hitPart.rb.velocity + Krakensbane.GetFrameVelocityV3f());
                            }

                            float hitAngle = Vector3.Angle(impactVector, -hit.normal);

                            if (ProjectileUtils.CheckGroundHit(hitPart, hit, caliber))
                            {
                                ProjectileUtils.CheckBuildingHit(hit, rocketMass, rb.velocity, bulletDmgMult);
                                Detonate(hit.point, false);
                                return;
                            }

                            impactVelocity = impactVector.magnitude;
                            if (gravitic)
                            {
                                var ME = hitPart.FindModuleImplementing <ModuleMassAdjust>();
                                if (ME == null)
                                {
                                    ME = (ModuleMassAdjust)hitPart.AddModule("ModuleMassAdjust");
                                }
                                ME.massMod  += massMod;
                                ME.duration += BDArmorySettings.WEAPON_FX_DURATION;
                            }
                            if (concussion)
                            {
                                hitPart.rb.AddForceAtPosition(impactVector.normalized * impulse, hit.point, ForceMode.Acceleration);
                                Detonate(hit.point, false);
                                hasDetonated = true;
                                return; //impulse rounds shouldn't penetrate/do damage
                            }
                            float anglemultiplier = (float)Math.Cos(Math.PI * hitAngle / 180.0);

                            float thickness         = ProjectileUtils.CalculateThickness(hitPart, anglemultiplier);
                            float penetration       = ProjectileUtils.CalculatePenetration(caliber, rocketMass, impactVelocity);
                            float penetrationFactor = ProjectileUtils.CalculateArmorPenetration(hitPart, anglemultiplier, hit, penetration, thickness, caliber);
                            if (penetration > thickness)
                            {
                                rb.velocity = rb.velocity * (float)Math.Sqrt(thickness / penetration);
                                if (penTicker > 0)
                                {
                                    rb.velocity *= 0.55f;
                                }
                            }

                            if (penetrationFactor > 1)
                            {
                                hasPenetrated = true;
                                ProjectileUtils.ApplyDamage(hitPart, hit, 1, penetrationFactor, caliber, rocketMass, impactVelocity, bulletDmgMult, distanceFromStart, explosive, incendiary, false, sourceVessel, rocketName, team);
                                penTicker += 1;
                                ProjectileUtils.CheckPartForExplosion(hitPart);

                                if (explosive)
                                {
                                    transform.position += (rb.velocity * Time.fixedDeltaTime) / 3;

                                    Detonate(transform.position, false);
                                    hasDetonated = true;
                                }
                            }
                            else // stopped by armor
                            {
                                if (hitPart.rb != null && hitPart.rb.mass > 0)
                                {
                                    float forceAverageMagnitude = impactVelocity * impactVelocity *
                                                                  (1f / hit.distance) * rocketMass;

                                    float accelerationMagnitude =
                                        forceAverageMagnitude / (hitPart.vessel.GetTotalMass() * 1000);

                                    hitPart.rb.AddForceAtPosition(impactVector.normalized * accelerationMagnitude, hit.point, ForceMode.Acceleration);

                                    if (BDArmorySettings.DRAW_DEBUG_LABELS)
                                    {
                                        Debug.Log("[BDArmory.PooledRocket]: Force Applied " + Math.Round(accelerationMagnitude, 2) + "| Vessel mass in kgs=" + hitPart.vessel.GetTotalMass() * 1000 + "| rocket effective mass =" + rocketMass);
                                    }
                                }

                                hasPenetrated = false;
                                ProjectileUtils.ApplyDamage(hitPart, hit, 1, penetrationFactor, caliber, rocketMass, impactVelocity, bulletDmgMult, distanceFromStart, explosive, incendiary, false, sourceVessel, rocketName, team);
                                Detonate(hit.point, false);
                                hasDetonated = true;
                            }

                            if (penTicker >= 2)
                            {
                                Detonate(hit.point, false);
                                return;
                            }

                            if (rb.velocity.magnitude <= 100 && hasPenetrated && (Time.time - startTime > thrustTime))
                            {
                                if (BDArmorySettings.DRAW_DEBUG_LABELS)
                                {
                                    Debug.Log("[BDArmory.PooledRocket]: Rocket ballistic velocity too low, stopping");
                                }
                                Detonate(hit.point, false);
                                return;
                            }
                            if (!hasPenetrated || hasDetonated)
                            {
                                break;
                            }
                        }
                    }
                }
            }
            else if (FlightGlobals.getAltitudeAtPos(currPosition) <= 0)
            {
                Detonate(currPosition, false);
            }
            prevPosition = currPosition;

            if (Time.time - startTime > lifeTime) // life's 10s, quite a long time for faster rockets
            {
                Detonate(transform.position, true);
            }
            if (distanceFromStart >= maxAirDetonationRange)//rockets are performance intensive, lets cull those that have flown too far away
            {
                Detonate(transform.position, false);
            }
            if (ProximityAirDetonation(distanceFromStart))
            {
                Detonate(transform.position, false);
            }
        }
        private bool Checkproximity(float distanceFromStart)
        {
            bool detonate = false;

            if (distanceFromStart < blastRadius)
            {
                return(detonate = false);
            }

            using (var hitsEnu = Physics.OverlapSphere(transform.position, detonationRange, 557057).AsEnumerable().GetEnumerator())
            {
                while (hitsEnu.MoveNext())
                {
                    if (hitsEnu.Current == null)
                    {
                        continue;
                    }

                    Part partHit = hitsEnu.Current.GetComponentInParent <Part>();
                    if (partHit == null || partHit.vessel == null)
                    {
                        continue;
                    }
                    if (ProjectileUtils.IsIgnoredPart(partHit))
                    {
                        continue;                                         // Ignore ignored parts.
                    }
                    if (partHit.vessel == vessel || partHit.vessel == sourcevessel)
                    {
                        continue;
                    }
                    if (partHit.vessel.vesselType == VesselType.Debris)
                    {
                        continue;
                    }
                    if (sourcevessel != null && partHit.vessel.vesselName.Contains(sourcevessel.vesselName))
                    {
                        continue;
                    }
                    var weaponManager = VesselModuleRegistry.GetModule <MissileFire>(partHit.vessel);
                    if (IFF_On && (weaponManager == null || weaponManager.teamString == IFFID))
                    {
                        continue;
                    }
                    if (detonateAtMinimumDistance)
                    {
                        var distance          = Vector3.Distance(partHit.transform.position + partHit.CoMOffset, transform.position);
                        var predictedDistance = Vector3.Distance(AIUtils.PredictPosition(partHit.transform.position + partHit.CoMOffset, partHit.vessel.Velocity(), partHit.vessel.acceleration, Time.fixedDeltaTime), AIUtils.PredictPosition(transform.position, vessel.Velocity(), vessel.acceleration, Time.fixedDeltaTime));
                        if (distance > predictedDistance && distance > Time.fixedDeltaTime * (float)vessel.srfSpeed) // If we're closing and not going to hit within the next update, then wait.
                        {
                            return(detonate = false);
                        }
                    }
                    if (BDArmorySettings.DRAW_DEBUG_LABELS)
                    {
                        Debug.Log("[BDArmory.BDExplosivePart]: Proxifuze triggered by " + partHit.partName + " from " + partHit.vessel.vesselName);
                    }
                    return(detonate = true);
                }
            }
            return(detonate);
        }