예제 #1
0
        protected override void Impact(Thing hitThing)
        {
            Map map = base.Map;

            base.Impact(hitThing);
            if (hitThing != null)
            {
                int                  damageAmountBase = def.projectile.damageAmountBase;
                ThingDef             equipmentDef     = this.equipmentDef;
                DamageDefExtensionCE damDefCE         = def.projectile.damageDef.GetModExtension <DamageDefExtensionCE>() ?? new DamageDefExtensionCE();

                DamageInfo dinfo = new DamageInfo(
                    def.projectile.damageDef,
                    damageAmountBase,
                    ExactRotation.eulerAngles.y,
                    launcher,
                    null,
                    def);

                // Set impact height
                BodyPartDepth  partDepth  = damDefCE != null && damDefCE.harmOnlyOutsideLayers ? BodyPartDepth.Outside : BodyPartDepth.Undefined;
                BodyPartHeight partHeight = new CollisionVertical(hitThing).GetCollisionBodyHeight(Height);
                dinfo.SetBodyRegion(partHeight, partDepth);
                if (damDefCE != null && damDefCE.harmOnlyOutsideLayers)
                {
                    dinfo.SetBodyRegion(BodyPartHeight.Undefined, BodyPartDepth.Outside);
                }

                // Apply primary damage
                hitThing.TakeDamage(dinfo);

                // Apply secondary to non-pawns (pawn secondary damage is handled in the damage worker)
                var projectilePropsCE = def.projectile as ProjectilePropertiesCE;
                if (!(hitThing is Pawn) && projectilePropsCE != null && !projectilePropsCE.secondaryDamage.NullOrEmpty())
                {
                    foreach (SecondaryDamage cur in projectilePropsCE.secondaryDamage)
                    {
                        if (hitThing.Destroyed)
                        {
                            break;
                        }
                        var secDinfo = new DamageInfo(
                            cur.def,
                            cur.amount,
                            ExactRotation.eulerAngles.y,
                            launcher,
                            null,
                            def);
                        hitThing.TakeDamage(secDinfo);
                    }
                }
            }
            else
            {
                SoundDefOf.BulletImpactGround.PlayOneShot(new TargetInfo(base.Position, map, false));
                MoteMaker.MakeStaticMote(ExactPosition, map, ThingDefOf.Mote_ShotHit_Dirt, 1f);
            }
        }
예제 #2
0
        /// <summary>
        /// Selects a random BodyPartHeight out of the ones our CasterPawn can hit, depending on our body size vs the target's. So a rabbit can hit top height of another rabbit, but not of a human.
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        private BodyPartHeight GetBodyPartHeightFor(LocalTargetInfo target)
        {
            Pawn pawn = target.Thing as Pawn;

            if (pawn == null || CasterPawn == null)
            {
                return(BodyPartHeight.Undefined);
            }
            var            casterReach  = new CollisionVertical(CasterPawn).Max * 1.2f;
            var            targetHeight = new CollisionVertical(pawn);
            BodyPartHeight maxHeight    = targetHeight.GetRandWeightedBodyHeightBelow(casterReach);
            BodyPartHeight height       = (BodyPartHeight)Rand.RangeInclusive(1, (int)maxHeight);

            return(height);
        }
예제 #3
0
        public static Bounds GetBoundsFor(Thing thing)
        {
            if (thing == null)
            {
                return(new Bounds());
            }
            var height   = new CollisionVertical(thing);
            var width    = GetCollisionWidth(thing);
            var thingPos = thing.DrawPos;

            thingPos.y = height.Max - height.HeightRange.Span / 2;
            Bounds bounds = new Bounds(thingPos, new Vector3(width, height.HeightRange.Span, width));

            return(bounds);
        }
예제 #4
0
        protected override void Impact(Thing hitThing)
        {
            Map map = base.Map;

            base.Impact(hitThing);
            if (hitThing != null)
            {
                int                  damageAmountBase = def.projectile.damageAmountBase;
                ThingDef             equipmentDef     = this.equipmentDef;
                DamageDefExtensionCE damDefCE         = def.projectile.damageDef.GetModExtension <DamageDefExtensionCE>() ?? new DamageDefExtensionCE();

                DamageInfo dinfo = new DamageInfo(
                    def.projectile.damageDef,
                    damageAmountBase,
                    ExactRotation.eulerAngles.y,
                    launcher,
                    null,
                    def);

                // Set impact height
                BodyPartDepth  partDepth  = damDefCE != null && damDefCE.harmOnlyOutsideLayers ? BodyPartDepth.Outside : BodyPartDepth.Undefined;
                BodyPartHeight partHeight = new CollisionVertical(hitThing).GetCollisionBodyHeight(Height);
                dinfo.SetBodyRegion(partHeight, partDepth);
                if (damDefCE != null && damDefCE.harmOnlyOutsideLayers)
                {
                    dinfo.SetBodyRegion(BodyPartHeight.Undefined, BodyPartDepth.Outside);
                }

                // Apply primary damage
                hitThing.TakeDamage(dinfo);
            }
            else
            {
                SoundDefOf.BulletImpactGround.PlayOneShot(new TargetInfo(base.Position, map, false));
                MoteMaker.MakeStaticMote(ExactPosition, map, ThingDefOf.Mote_ShotHit_Dirt, 1f);
            }
        }
예제 #5
0
        /// <summary>
        /// Checks for cover along the flight path of the bullet, doesn't check for walls or trees, only intended for cover with partial fillPercent
        /// </summary>
        /// <param name="target">The target of which to find cover of</param>
        /// <param name="cover">Output parameter, filled with the highest cover object found</param>
        /// <returns>True if cover was found, false otherwise</returns>
        private bool GetHighestCoverAndSmokeForTarget(LocalTargetInfo target, out Thing cover, out float smokeDensity)
        {
            Map   map                = caster.Map;
            Thing targetThing        = target.Thing;
            Thing highestCover       = null;
            float highestCoverHeight = 0f;

            smokeDensity = 0;

            // Iterate through all cells on line of sight and check for cover and smoke
            var cells = GenSight.PointsOnLineOfSight(target.Cell, caster.Position).ToArray();

            if (cells.Length < 3)
            {
                cover = null;
                return(false);
            }
            for (int i = 0; i <= cells.Length / 2; i++)
            {
                var cell = cells[i];

                if (cell.AdjacentTo8Way(caster.Position))
                {
                    continue;
                }

                // Check for smoke
                var gas = cell.GetGas(map);
                if (gas != null)
                {
                    smokeDensity += gas.def.gas.accuracyPenalty;
                }

                // Check for cover in the second half of LoS
                if (i <= cells.Length / 2)
                {
                    Pawn  pawn     = cell.GetFirstPawn(map);
                    Thing newCover = pawn == null?cell.GetCover(map) : pawn;

                    float newCoverHeight = new CollisionVertical(newCover).Max;

                    // Cover check, if cell has cover compare collision height and get the highest piece of cover, ignore if cover is the target (e.g. solar panels, crashed ship, etc)
                    if (newCover != null &&
                        (targetThing == null || !newCover.Equals(targetThing)) &&
                        (highestCover == null || highestCoverHeight < newCoverHeight) &&
                        newCover.def.Fillage == FillCategory.Partial &&
                        !newCover.IsPlant())
                    {
                        highestCover       = newCover;
                        highestCoverHeight = newCoverHeight;
                        if (Controller.settings.DebugDrawTargetCoverChecks)
                        {
                            map.debugDrawer.FlashCell(cell, highestCoverHeight, highestCoverHeight.ToString());
                        }
                    }
                }
            }
            cover = highestCover;

            //Report success if found cover
            return(cover != null);
        }
예제 #6
0
        /// <summary>
        /// Shifts the original target position in accordance with target leading, range estimation and weather/lighting effects
        /// </summary>
        protected virtual void ShiftTarget(ShiftVecReport report, bool calculateMechanicalOnly = false)
        {
            if (!calculateMechanicalOnly)
            {
                Vector3 u = caster.TrueCenter();
                sourceLoc.Set(u.x, u.z);

                if (numShotsFired == 0)
                {
                    // On first shot of burst do a range estimate
                    estimatedTargDist = report.GetRandDist();
                }
                Vector3 v = report.target.Thing?.TrueCenter() ?? report.target.Cell.ToVector3Shifted(); //report.targetPawn != null ? report.targetPawn.DrawPos + report.targetPawn.Drawer.leaner.LeanOffset * 0.5f : report.target.Cell.ToVector3Shifted();
                if (report.targetPawn != null)
                {
                    v += report.targetPawn.Drawer.leaner.LeanOffset * 0.5f;
                }

                newTargetLoc.Set(v.x, v.z);

                // ----------------------------------- STEP 1: Actual location + Shift for visibility

                //FIXME : GetRandCircularVec may be causing recoil to be unnoticeable - each next shot in the burst has a new random circular vector around the target.
                newTargetLoc += report.GetRandCircularVec();

                // ----------------------------------- STEP 2: Estimated shot to hit location

                newTargetLoc = sourceLoc + (newTargetLoc - sourceLoc).normalized * estimatedTargDist;

                // Lead a moving target
                newTargetLoc += report.GetRandLeadVec();

                // ----------------------------------- STEP 3: Recoil, Skewing, Skill checks, Cover calculations

                rotationDegrees = 0f;
                angleRadians    = 0f;

                GetSwayVec(ref rotationDegrees, ref angleRadians);
                GetRecoilVec(ref rotationDegrees, ref angleRadians);

                // Height difference calculations for ShotAngle
                float targetHeight = 0f;

                var coverRange = new CollisionVertical(report.cover).HeightRange;   //Get " " cover, assume it is the edifice

                // Projectiles with flyOverhead target the surface in front of the target
                if (Projectile.projectile.flyOverhead)
                {
                    targetHeight = coverRange.max;
                }
                else
                {
                    var victimVert  = new CollisionVertical(currentTarget.Thing);
                    var targetRange = victimVert.HeightRange;   //Get lower and upper heights of the target

                    /*if (currentTarget.Thing is Building && CompFireModes?.CurrentAimMode == AimMode.SuppressFire)
                     * {
                     *  targetRange.min = targetRange.max;
                     *  targetRange.max = targetRange.min + 1f;
                     * }*/
                    if (targetRange.min < coverRange.max)   //Some part of the target is hidden behind some cover
                    {
                        // - It is possible for targetRange.max < coverRange.max, technically, in which case the shooter will never hit until the cover is gone.
                        // - This should be checked for in LoS -NIA
                        targetRange.min = coverRange.max;

                        // Target fully hidden, shift aim upwards if we're doing suppressive fire
                        if (targetRange.max <= coverRange.max && CompFireModes?.CurrentAimMode == AimMode.SuppressFire)
                        {
                            targetRange.max = coverRange.max * 2;
                        }
                    }
                    else if (currentTarget.Thing is Pawn)
                    {
                        // Aim for center of mass on an exposed target
                        targetRange.min = victimVert.BottomHeight;
                        targetRange.max = victimVert.MiddleHeight;
                    }
                    targetHeight = VerbPropsCE.ignorePartialLoSBlocker ? 0 : targetRange.Average;
                }
                angleRadians += ProjectileCE.GetShotAngle(ShotSpeed, (newTargetLoc - sourceLoc).magnitude, targetHeight - ShotHeight, Projectile.projectile.flyOverhead, projectilePropsCE.Gravity);
            }

            // ----------------------------------- STEP 4: Mechanical variation

            // Get shotvariation, in angle Vector2 RADIANS.
            Vector2 spreadVec = report.GetRandSpreadVec();

            // ----------------------------------- STEP 5: Finalization

            var w = (newTargetLoc - sourceLoc);

            shotRotation = (-90 + Mathf.Rad2Deg * Mathf.Atan2(w.y, w.x) + rotationDegrees + spreadVec.x) % 360;
            shotAngle    = angleRadians + spreadVec.y * Mathf.Deg2Rad;
        }
예제 #7
0
        protected override void Impact(Thing hitThing)
        {
            Map map = base.Map;
            BattleLogEntry_RangedImpact logEntry = null;

            if (logMisses
                ||
                (!logMisses &&
                 hitThing != null &&
                 (hitThing is Pawn ||
                  hitThing is Building_Turret)
                ))
            {
                LogImpact(hitThing, out logEntry);
            }

            if (hitThing != null)
            {
                int damageAmountBase          = def.projectile.damageAmountBase;
                DamageDefExtensionCE damDefCE = def.projectile.damageDef.GetModExtension <DamageDefExtensionCE>() ?? new DamageDefExtensionCE();

                DamageInfo dinfo = new DamageInfo(
                    def.projectile.damageDef,
                    damageAmountBase,
                    ExactRotation.eulerAngles.y,
                    launcher,
                    null,
                    def);

                // Set impact height
                BodyPartDepth partDepth = damDefCE != null && damDefCE.harmOnlyOutsideLayers ? BodyPartDepth.Outside : BodyPartDepth.Undefined;
                //NOTE: ExactPosition.y isn't always Height at the point of Impact!
                BodyPartHeight partHeight = new CollisionVertical(hitThing).GetCollisionBodyHeight(ExactPosition.y);
                dinfo.SetBodyRegion(partHeight, partDepth);
                if (damDefCE != null && damDefCE.harmOnlyOutsideLayers)
                {
                    dinfo.SetBodyRegion(BodyPartHeight.Undefined, BodyPartDepth.Outside);
                }

                // Apply primary damage
                hitThing.TakeDamage(dinfo).InsertIntoLog(logEntry);

                // Apply secondary to non-pawns (pawn secondary damage is handled in the damage worker)
                var projectilePropsCE = def.projectile as ProjectilePropertiesCE;
                if (!(hitThing is Pawn) && projectilePropsCE != null && !projectilePropsCE.secondaryDamage.NullOrEmpty())
                {
                    foreach (SecondaryDamage cur in projectilePropsCE.secondaryDamage)
                    {
                        if (hitThing.Destroyed)
                        {
                            break;
                        }
                        var secDinfo = new DamageInfo(
                            cur.def,
                            cur.amount,
                            ExactRotation.eulerAngles.y,
                            launcher,
                            null,
                            def);
                        hitThing.TakeDamage(secDinfo).InsertIntoLog(logEntry);
                    }
                }
            }
            else
            {
                SoundDefOf.BulletImpactGround.PlayOneShot(new TargetInfo(base.Position, map, false));

                //Only display a dirt hit for projectiles with a dropshadow
                if (base.castShadow)
                {
                    MoteMaker.MakeStaticMote(ExactPosition, map, ThingDefOf.Mote_ShotHit_Dirt, 1f);
                }
            }
            base.Impact(hitThing);
        }
예제 #8
0
        private static void CalculateHeightRange(Thing thing, out FloatRange heightRange, out float shotHeight)
        {
            shotHeight  = 0;
            heightRange = new FloatRange(0, 0);
            if (thing == null)
            {
                return;
            }

            var plant = thing as Plant;

            if (plant != null)
            {
                //Height matches up exactly with visual size
                heightRange = new FloatRange(0f, BoundsInjector.ForPlant(plant).y);
                return;
            }

            if (thing is Building)
            {
                if (thing is Building_Door door && door.Open)
                {
                    return;             //returns heightRange = (0,0) & shotHeight = 0. If not open, doors have FillCategory.Full so returns (0, WallCollisionHeight)
                }

                if (thing.def.Fillage == FillCategory.Full)
                {
                    heightRange = new FloatRange(0, WallCollisionHeight);
                    shotHeight  = WallCollisionHeight;
                    return;
                }
                float fillPercent = thing.def.fillPercent;
                heightRange = new FloatRange(Mathf.Min(0f, fillPercent), Mathf.Max(0f, fillPercent));
                shotHeight  = fillPercent;
                return;
            }

            float collisionHeight  = 0f;
            float shotHeightOffset = 0;
            var   pawn             = thing as Pawn;

            if (pawn != null)
            {
                collisionHeight = CE_Utility.GetCollisionBodyFactors(pawn).y;

                shotHeightOffset = collisionHeight * (1 - BodyRegionMiddleHeight);

                // Humanlikes in combat crouch to reduce their profile
                if (pawn.IsCrouching())
                {
                    float crouchHeight = BodyRegionBottomHeight * collisionHeight;  // Minimum height we can crouch down to

                    // Find the highest adjacent cover
                    Map map = pawn.Map;
                    foreach (IntVec3 curCell in GenAdjFast.AdjacentCells8Way(pawn.Position))
                    {
                        if (curCell.InBounds(map))
                        {
                            Thing cover = curCell.GetCover(map);
                            if (cover != null && cover.def.Fillage == FillCategory.Partial && !cover.IsPlant())
                            {
                                var coverHeight = new CollisionVertical(cover).Max;
                                if (coverHeight > crouchHeight)
                                {
                                    crouchHeight = coverHeight;
                                }
                            }
                        }
                    }
                    collisionHeight = Mathf.Min(collisionHeight, crouchHeight + 0.01f + shotHeightOffset);  // We crouch down only so far that we can still shoot over our own cover and never beyond our own body size
                }
            }
            else
            {
                collisionHeight = thing.def.fillPercent;
            }
            var edificeHeight = 0f;

            if (thing.Map != null)
            {
                var edifice = thing.Position.GetCover(thing.Map);
                if (edifice != null && edifice.GetHashCode() != thing.GetHashCode() && !edifice.IsPlant())
                {
                    edificeHeight = new CollisionVertical(edifice).heightRange.max;
                }
            }
            float fillPercent2 = collisionHeight;

            heightRange = new FloatRange(Mathf.Min(edificeHeight, edificeHeight + fillPercent2), Mathf.Max(edificeHeight, edificeHeight + fillPercent2));
            shotHeight  = heightRange.max - shotHeightOffset;
        }
예제 #9
0
        public override void Impact(Thing hitThing)
        {
            bool cookOff = (launcher is AmmoThing);

            Map map = base.Map;
            LogEntry_DamageResult logEntry = null;

            if (!cookOff && (logMisses || hitThing is Pawn || hitThing is Building_Turret))
            {
                LogImpact(hitThing, out logEntry);
            }

            if (hitThing != null)
            {
                // launcher being the pawn equipping the weapon, not the weapon itself
                float damageAmountBase  = DamageAmount;
                var   projectilePropsCE = (ProjectilePropertiesCE)def.projectile;
                var   isSharpDmg        = def.projectile.damageDef.armorCategory == DamageArmorCategoryDefOf.Sharp;
                var   penetration       = PenetrationAmount;
                var   damDefCE          = def.projectile.damageDef.GetModExtension <DamageDefExtensionCE>() ?? new DamageDefExtensionCE();
                var   dinfo             = new DamageInfo(
                    def.projectile.damageDef,
                    damageAmountBase,
                    penetration,       //Armor Penetration
                    ExactRotation.eulerAngles.y,
                    launcher,
                    null,
                    def);

                // Set impact height
                BodyPartDepth partDepth = damDefCE.harmOnlyOutsideLayers ? BodyPartDepth.Outside : BodyPartDepth.Undefined;
                //NOTE: ExactPosition.y isn't always Height at the point of Impact!
                BodyPartHeight partHeight = new CollisionVertical(hitThing).GetCollisionBodyHeight(ExactPosition.y);
                dinfo.SetBodyRegion(partHeight, partDepth);
                if (damDefCE.harmOnlyOutsideLayers)
                {
                    dinfo.SetBodyRegion(BodyPartHeight.Undefined, BodyPartDepth.Outside);
                }

                //The following code excludes turrets etcetera from having cook off projectile impacts recorded in their combat log.
                //If it is necessary to add cook off to turret logs, a new BattleLogEntry_ must be created, because BattleLogEntry_DamageTaken,
                //which is the only method capable of handling cookoff and only using pawns, can not take !(hitThing is Pawn).
                if (cookOff && hitThing is Pawn hitPawn)
                {
                    logEntry =
                        new BattleLogEntry_DamageTaken(
                            hitPawn,
                            CookOff
                            );
                    Find.BattleLog.Add(logEntry);
                }

                try
                {
                    // Apply primary damage
                    hitThing.TakeDamage(dinfo).AssociateWithLog(logEntry);

                    // Apply secondary to non-pawns (pawn secondary damage is handled in the damage worker)
                    // The !(hitThing is Pawn) already excludes non-pawn cookoff projectiles from being logged, as logEntry == null
                    if (!(hitThing is Pawn) && projectilePropsCE != null && !projectilePropsCE.secondaryDamage.NullOrEmpty())
                    {
                        foreach (SecondaryDamage cur in projectilePropsCE.secondaryDamage)
                        {
                            if (hitThing.Destroyed || !Rand.Chance(cur.chance))
                            {
                                break;
                            }

                            var secDinfo = cur.GetDinfo(dinfo);
                            hitThing.TakeDamage(secDinfo).AssociateWithLog(logEntry);
                        }
                    }
                }
                catch (Exception e)
                {
                    Log.Error("CombatExtended :: BulletCE impacting thing " + hitThing.LabelCap + " of def " + hitThing.def.LabelCap + " added by mod " + hitThing.def.modContentPack.Name + ". See following stacktrace for information.");
                    throw e;
                }
                finally
                {
                    base.Impact(hitThing);
                }
            }
            else
            {
                SoundDefOf.BulletImpact_Ground.PlayOneShot(new TargetInfo(base.Position, map, false));

                //Only display a dirt/water hit for projectiles with a dropshadow
                if (base.castShadow)
                {
                    FleckMaker.Static(this.ExactPosition, map, FleckDefOf.ShotHit_Dirt, 1f);
                    if (base.Position.GetTerrain(map).takeSplashes)
                    {
                        FleckMaker.WaterSplash(this.ExactPosition, map, Mathf.Sqrt(def.projectile.GetDamageAmount(this.launcher)) * 1f, 4f);
                    }
                }
                base.Impact(null);
            }
            NotifyImpact(hitThing, map, Position);
        }
예제 #10
0
        protected override void Impact(Thing hitThing)
        {
            bool cookOff = (launcher is AmmoThing);

            Map map = base.Map;
            LogEntry_DamageResult logEntry = null;

            if (logMisses
                ||
                (!logMisses &&
                 hitThing != null &&
                 (hitThing is Pawn ||
                  hitThing is Building_Turret)
                ))
            {
                if (!cookOff)
                {
                    LogImpact(hitThing, out logEntry);
                }
            }

            if (hitThing != null)
            {
                // launcher being the pawn equipping the weapon, not the weapon itself
                int damageAmountBase          = def.projectile.GetDamageAmount(1);
                DamageDefExtensionCE damDefCE = def.projectile.damageDef.GetModExtension <DamageDefExtensionCE>() ?? new DamageDefExtensionCE();
                var projectilePropsCE         = (ProjectilePropertiesCE)def.projectile;

                DamageInfo dinfo = new DamageInfo(
                    def.projectile.damageDef,
                    damageAmountBase,
                    projectilePropsCE.GetArmorPenetration(1), //Armor Penetration
                    ExactRotation.eulerAngles.y,
                    launcher,
                    null,
                    def);

                // Set impact height
                BodyPartDepth partDepth = damDefCE != null && damDefCE.harmOnlyOutsideLayers ? BodyPartDepth.Outside : BodyPartDepth.Undefined;
                //NOTE: ExactPosition.y isn't always Height at the point of Impact!
                BodyPartHeight partHeight = new CollisionVertical(hitThing).GetCollisionBodyHeight(ExactPosition.y);
                dinfo.SetBodyRegion(partHeight, partDepth);
                if (damDefCE != null && damDefCE.harmOnlyOutsideLayers)
                {
                    dinfo.SetBodyRegion(BodyPartHeight.Undefined, BodyPartDepth.Outside);
                }

                //The following code excludes turrets etcetera from having cook off projectile impacts recorded in their combat log.
                //If it is necessary to add cook off to turret logs, a new BattleLogEntry_ must be created, because BattleLogEntry_DamageTaken,
                //which is the only method capable of handling cookoff and only using pawns, can not take !(hitThing is Pawn).
                if (cookOff && hitThing is Pawn)
                {
                    logEntry =
                        new BattleLogEntry_DamageTaken(
                            (Pawn)hitThing,
                            DefDatabase <RulePackDef> .GetNamed("DamageEvent_CookOff"));
                    Find.BattleLog.Add(logEntry);
                }

                // Apply primary damage
                hitThing.TakeDamage(dinfo).AssociateWithLog(logEntry);

                // Apply secondary to non-pawns (pawn secondary damage is handled in the damage worker)
                // The !(hitThing is Pawn) already excludes non-pawn cookoff projectiles from being logged, as logEntry == null
                if (!(hitThing is Pawn) && projectilePropsCE != null && !projectilePropsCE.secondaryDamage.NullOrEmpty())
                {
                    foreach (SecondaryDamage cur in projectilePropsCE.secondaryDamage)
                    {
                        if (hitThing.Destroyed)
                        {
                            break;
                        }
                        var secDinfo = new DamageInfo(
                            cur.def,
                            cur.amount,
                            projectilePropsCE.GetArmorPenetration(1), //Armor Penetration
                            ExactRotation.eulerAngles.y,
                            launcher,
                            null,
                            def
                            );
                        hitThing.TakeDamage(secDinfo).AssociateWithLog(logEntry);
                    }
                }
            }
            else
            {
                SoundDefOf.BulletImpact_Ground.PlayOneShot(new TargetInfo(base.Position, map, false));

                //Only display a dirt/water hit for projectiles with a dropshadow
                if (base.castShadow)
                {
                    MoteMaker.MakeStaticMote(this.ExactPosition, map, ThingDefOf.Mote_ShotHit_Dirt, 1f);
                    if (base.Position.GetTerrain(map).takeSplashes)
                    {
                        MoteMaker.MakeWaterSplash(this.ExactPosition, map, Mathf.Sqrt(def.projectile.GetDamageAmount(this.launcher)) * 1f, 4f);
                    }
                }
            }
            base.Impact(hitThing);
        }