Exemple #1
0
        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 hte hydra, which the randomTurstdeviation was originally calibrated for
            }

            if (Time.time - startTime > thrustTime)
            {
                foreach (var pe in pEmitters)
                {
                    if (pe != null)
                    {
                        pe.emit = false;
                    }
                }
            }
            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 (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, false, sourceVessel, rocketName);
                                break;
                            }

                            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, false, sourceVessel, rocketName);
                                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, false, sourceVessel, rocketName);
                                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);
            }
        }
        /// <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);
        }