Пример #1
0
        public virtual ShiftVecReport ShiftVecReportFor(LocalTargetInfo target)
        {
            IntVec3        targetCell = target.Cell;
            ShiftVecReport report     = new ShiftVecReport();

            report.target           = target;
            report.aimingAccuracy   = this.AimingAccuracy;
            report.sightsEfficiency = this.SightsEfficiency;
            report.shotDist         = (targetCell - this.caster.Position).LengthHorizontal;
            report.maxRange         = verbProps.range;

            report.lightingShift = 1 - caster.Map.glowGrid.GameGlowAt(targetCell);
            if (!this.caster.Position.Roofed(caster.Map) || !targetCell.Roofed(caster.Map))  //Change to more accurate algorithm?
            {
                report.weatherShift = 1 - caster.Map.weatherManager.CurWeatherAccuracyMultiplier;
            }
            report.shotSpeed   = this.ShotSpeed;
            report.swayDegrees = this.SwayAmplitude;
            var spreadmult = this.projectilePropsCE != null ? this.projectilePropsCE.spreadMult : 0f;

            report.spreadDegrees = this.EquipmentSource.GetStatValue(StatDef.Named("ShotSpread")) * spreadmult;
            Thing cover;
            float smokeDensity;

            this.GetHighestCoverAndSmokeForTarget(target, out cover, out smokeDensity);
            report.cover        = cover;
            report.smokeDensity = smokeDensity;

            return(report);
        }
Пример #2
0
        public override ShiftVecReport ShiftVecReportFor(LocalTargetInfo target)
        {
            ShiftVecReport report = base.ShiftVecReportFor(target);

            report.shotDist = Vector3.Distance(target.CenterVector3, caster.TrueCenter()) * DISTFACTOR_FACTOR;
            return(report);
        }
        /// <summary>
        /// Fires a projectile using the new aiming system
        /// </summary>
        /// <returns>True for successful shot, false otherwise</returns>
        protected override bool TryCastShot()
        {
            ShootLine shootLine;

            if (!TryFindCEShootLineFromTo(caster.Position, currentTarget, out shootLine))
            {
                return(false);
            }
            if (projectilePropsCE.pelletCount < 1)
            {
                Log.Error(ownerEquipment.LabelCap + " tried firing with pelletCount less than 1.");
                return(false);
            }
            ShiftVecReport report = ShiftVecReportFor(currentTarget);
            bool           pelletMechanicsOnly = false;

            for (int i = 0; i < projectilePropsCE.pelletCount; i++)
            {
                ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(ProjectileDef, null);
                GenSpawn.Spawn(projectile, shootLine.Source, caster.Map);
                ShiftTarget(report, pelletMechanicsOnly);

                //New aiming algorithm
                projectile.canTargetSelf   = false;
                projectile.minCollisionSqr = (sourceLoc - currentTarget.Cell.ToIntVec2.ToVector2Shifted()).sqrMagnitude;
                projectile.Launch(caster, sourceLoc, shotAngle, shotRotation, ShotHeight, ShotSpeed, ownerEquipment);
                pelletMechanicsOnly = true;
            }
            pelletMechanicsOnly = false;
            this.numShotsFired++;
            return(true);
        }
        public override bool TryCastShot()
        {
            ArtilleryMarker marker = ThingMaker.MakeThing(ThingDef.Named(ArtilleryMarker.MarkerDef)) as ArtilleryMarker;
            ShiftVecReport  report = ShiftVecReportFor(currentTarget);

            marker.sightsEfficiency = report.sightsEfficiency;
            marker.aimingAccuracy   = report.aimingAccuracy;
            marker.lightingShift    = report.lightingShift;
            marker.weatherShift     = report.weatherShift;

            GenSpawn.Spawn(marker, this.currentTarget.Cell, caster.Map);

            // Check for something to attach marker to
            if (this.currentTarget.HasThing)
            {
                CompAttachBase comp = this.currentTarget.Thing.TryGetComp <CompAttachBase>();
                if (comp != null)
                {
                    marker.AttachTo(this.currentTarget.Thing);
                }
            }
            // Show we learned something
            PlayerKnowledgeDatabase.KnowledgeDemonstrated(CE_ConceptDefOf.CE_Spotting, KnowledgeAmount.SmallInteraction);

            return(true);
        }
Пример #5
0
        /// <summary>
        /// Fires a projectile using the new aiming system
        /// </summary>
        /// <returns>True for successful shot, false otherwise</returns>
        protected override bool TryCastShot()
        {
            if (!TryFindCEShootLineFromTo(caster.Position, currentTarget, out var shootLine))
            {
                return(false);
            }
            if (projectilePropsCE.pelletCount < 1)
            {
                Log.Error(EquipmentSource.LabelCap + " tried firing with pelletCount less than 1.");
                return(false);
            }
            ShiftVecReport report = ShiftVecReportFor(currentTarget);
            bool           pelletMechanicsOnly = false;

            for (int i = 0; i < projectilePropsCE.pelletCount; i++)
            {
                ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(Projectile, null);
                GenSpawn.Spawn(projectile, shootLine.Source, caster.Map);
                ShiftTarget(report, pelletMechanicsOnly);

                //New aiming algorithm
                projectile.canTargetSelf = false;

                var targDist = (sourceLoc - currentTarget.Cell.ToIntVec2.ToVector2Shifted()).magnitude;
                if (targDist <= 2)
                {
                    targDist *= 2;  // Double to account for divide by 4 in ProjectileCE minimum collision distance calculations
                }
                projectile.minCollisionSqr = Mathf.Pow(targDist, 2);
                projectile.intendedTarget  = currentTarget.Thing;
                projectile.mount           = caster.Position.GetThingList(caster.Map).FirstOrDefault(t => t is Pawn && t != caster);
                projectile.AccuracyFactor  = report.accuracyFactor * report.swayDegrees * ((numShotsFired + 1) * 0.75f);
                projectile.Launch(
                    Shooter,    //Shooter instead of caster to give turret operators' records the damage/kills obtained
                    sourceLoc,
                    shotAngle,
                    shotRotation,
                    ShotHeight,
                    ShotSpeed,
                    EquipmentSource
                    );
                pelletMechanicsOnly = true;
            }
            /// Log.Message("Fired from "+caster.ThingID+" at "+ShotHeight); ///
            pelletMechanicsOnly = false;
            numShotsFired++;
            if (CompAmmo != null && !CompAmmo.CanBeFiredNow)
            {
                CompAmmo?.TryStartReload();
            }
            if (CompReloadable != null)
            {
                CompReloadable.UsedOnce();
            }
            return(true);
        }
Пример #6
0
 // Copy-constructor
 public ShiftVecReport(ShiftVecReport report)
 {
     target             = report.target;
     aimEfficiency      = report.aimEfficiency;
     aimingAccuracy     = report.aimingAccuracy;
     circularMissRadius = report.circularMissRadius;
     indirectFireShift  = report.indirectFireShift;
     lightingShift      = report.lightingShift;
     shotSpeed          = report.shotSpeed;
     shotDist           = report.shotDist;
     isAiming           = report.isAiming;
     swayDegrees        = report.swayDegrees;
     spreadDegrees      = report.spreadDegrees;
     cover = report.cover;
 }
Пример #7
0
        public virtual ShiftVecReport ShiftVecReportFor(LocalTargetInfo target)
        {
            IntVec3        targetCell = target.Cell;
            ShiftVecReport report     = new ShiftVecReport();

            report.target           = target;
            report.aimingAccuracy   = AimingAccuracy;
            report.sightsEfficiency = SightsEfficiency;
            report.shotDist         = (targetCell - caster.Position).LengthHorizontal;
            report.maxRange         = verbProps.range;
            report.lightingShift    = CE_Utility.GetLightingShift(caster, LightingTracker.CombatGlowAtFor(caster.Position, targetCell));

            if (!caster.Position.Roofed(caster.Map) || !targetCell.Roofed(caster.Map))  //Change to more accurate algorithm?
            {
                report.weatherShift = 1 - caster.Map.weatherManager.CurWeatherAccuracyMultiplier;
            }
            report.shotSpeed   = ShotSpeed;
            report.swayDegrees = SwayAmplitude;
            var spreadmult = projectilePropsCE != null ? projectilePropsCE.spreadMult : 0f;

            report.spreadDegrees = (EquipmentSource?.GetStatValue(StatDef.Named("ShotSpread")) ?? 0) * spreadmult;
            Thing cover;
            float smokeDensity;

            GetHighestCoverAndSmokeForTarget(target, out cover, out smokeDensity);
            report.cover        = cover;
            report.smokeDensity = smokeDensity;

            if (Controller.settings.DebugVerbose)
            {
                Log.Message($"<color=red>CE</color>: <color=orange>{caster}</color> shooting <color=orange>{target.Thing}</color> <color=yellow>ShiftVecReport</color>\n" +
                            $"1- aimingAccuracy:{report.aimingAccuracy}\n" +
                            $"2- sightsEfficiency:{report.sightsEfficiency}\n" +
                            $"3- maxRange:{report.maxRange}\n" +
                            $"4- lightingShift:{report.lightingShift}\n" +
                            $"5- spreadDegrees:{report.spreadDegrees}\n" +
                            $"6- smokeDensity:{report.smokeDensity}\n" +
                            $"7- swayDegrees:{report.swayDegrees}\n" +
                            $"8- shotSpeed:{report.shotSpeed}\n" +
                            $"9- shotDist:{report.shotDist}\n");
            }
            return(report);
        }
        /// <summary>
        /// Fires a projectile using the new aiming system
        /// </summary>
        /// <returns>True for successful shot, false otherwise</returns>
        protected override bool TryCastShot()
        {
            if (!TryFindCEShootLineFromTo(caster.Position, currentTarget, out var shootLine))
            {
                return(false);
            }
            if (projectilePropsCE.pelletCount < 1)
            {
                Log.Error(EquipmentSource.LabelCap + " tried firing with pelletCount less than 1.");
                return(false);
            }
            ShiftVecReport report = ShiftVecReportFor(currentTarget);
            bool           pelletMechanicsOnly = false;

            for (int i = 0; i < projectilePropsCE.pelletCount; i++)
            {
                ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(Projectile, null);
                GenSpawn.Spawn(projectile, shootLine.Source, caster.Map);
                ShiftTarget(report, pelletMechanicsOnly);

                //New aiming algorithm
                projectile.canTargetSelf   = false;
                projectile.minCollisionSqr = (sourceLoc - currentTarget.Cell.ToIntVec2.ToVector2Shifted()).sqrMagnitude;
                projectile.intendedTarget  = currentTarget.Thing;
                projectile.mount           = caster.Position.GetThingList(caster.Map).FirstOrDefault(t => t is Pawn && t != caster);
                projectile.AccuracyFactor  = report.accuracyFactor * report.swayDegrees * ((numShotsFired + 1) * 0.75f);
                projectile.Launch(
                    Shooter,    //Shooter instead of caster to give turret operators' records the damage/kills obtained
                    sourceLoc,
                    shotAngle,
                    shotRotation,
                    ShotHeight,
                    ShotSpeed,
                    EquipmentSource
                    );
                pelletMechanicsOnly = true;
            }
            pelletMechanicsOnly = false;
            numShotsFired++;
            return(true);
        }
        /// <summary>
        /// Fires a projectile using the new aiming system
        /// </summary>
        /// <returns>True for successful shot, false otherwise</returns>
        protected override bool TryCastShot()
        {
            ShootLine shootLine;

            if (!base.TryFindShootLineFromTo(this.caster.Position, this.currentTarget, out shootLine))
            {
                return(false);
            }
            if (this.projectilePropsCE.pelletCount < 1)
            {
                Log.Error(this.ownerEquipment.LabelCap + " tried firing with pelletCount less than 1.");
                return(false);
            }
            for (int i = 0; i < this.projectilePropsCE.pelletCount; i++)
            {
                Vector3      casterExactPosition = this.caster.DrawPos;
                ProjectileCE projectile          = (ProjectileCE)ThingMaker.MakeThing(projectileDef, null);
                GenSpawn.Spawn(projectile, shootLine.Source, caster.Map);
                float lengthHorizontalSquared = (this.currentTarget.Cell - this.caster.Position).LengthHorizontalSquared;

                //New aiming algorithm
                projectile.canFreeIntercept = true;
                ShiftVecReport report     = ShiftVecReportFor(this.currentTarget);
                Vector3        targetVec3 = this.ShiftTarget(report);
                projectile.shotAngle  = this.shotAngle;
                projectile.shotHeight = this.shotHeight;
                projectile.shotSpeed  = this.shotSpeed;
                if (this.currentTarget.Thing != null)
                {
                    projectile.Launch(this.caster, casterExactPosition, new LocalTargetInfo(this.currentTarget.Thing), targetVec3, this.ownerEquipment);
                }
                else
                {
                    projectile.Launch(this.caster, casterExactPosition, new LocalTargetInfo(shootLine.Dest), targetVec3, this.ownerEquipment);
                }
            }
            this.numShotsFired++;
            return(true);
        }
Пример #10
0
        protected override bool TryCastShot()
        {
            ArtilleryMarker marker = ThingMaker.MakeThing(ThingDef.Named(ArtilleryMarker.MarkerDef)) as ArtilleryMarker;
            ShiftVecReport  report = ShiftVecReportFor(currentTarget);

            marker.aimEfficiency  = report.aimEfficiency;
            marker.aimingAccuracy = report.aimingAccuracy;
            marker.lightingShift  = report.lightingShift;
            marker.weatherShift   = report.weatherShift;

            GenSpawn.Spawn(marker, this.currentTarget.Cell, caster.Map);

            // Check for something to attach marker to
            if (this.currentTarget.HasThing)
            {
                CompAttachBase comp = this.currentTarget.Thing.TryGetComp <CompAttachBase>();
                if (comp != null)
                {
                    marker.AttachTo(this.currentTarget.Thing);
                }
            }
            return(true);
        }
Пример #11
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;
        }
        /// <summary>
        /// Fires a projectile using the new aiming system
        /// </summary>
        /// <returns>True for successful shot, false otherwise</returns>
        protected override bool TryCastShot()
        {
            ShootLine shootLine;

            if (!TryFindCEShootLineFromTo(caster.Position, currentTarget, out shootLine))
            {
                return(false);
            }

            Pawn p = CasterPawn != null ? CasterPawn : caster as Pawn;


            if (caster != null)
            {
                //        Log.Message("caster: " + caster.ToString() + " pos: " + caster.Position.ToString());
            }
            if (CasterPawn != null)
            {
                //        Log.Message("casterPawn: " + CasterPawn.ToString() + " pos: " + CasterPawn.Position.ToString());
            }
            //    Log.Message("currentTarget: " + currentTarget.ToString() + " pos: " + currentTarget.Cell.ToString());



            if (p != null && !p.IsColonistPlayerControlled && p.equipment != null && p.equipment.Primary != null)
            {
                if (numShotsFired == 0)
                {
                    CompFireModes compFireModes = p.equipment.Primary.TryGetComp <CompFireModes>();
                    if (compFireModes != null)
                    {
                        float lengthToTarget  = Mathf.Abs((currentTarget.Cell - caster.Position).LengthHorizontal);
                        float rangemultiplier = this.VerbPropsCE.range / 60;

                        if (lengthToTarget <= (30 * rangemultiplier))
                        {
                            if (compFireModes.availableAimModes.Contains(AimMode.Snapshot) && compFireModes.currentAimModeInt != AimMode.Snapshot)
                            {
                                compFireModes.currentAimModeInt = AimMode.Snapshot;
                                //    Log.Message("selected aimmode1: " + compFireModes.currentAimModeInt);
                            }
                        }
                        else
                        {
                            if (compFireModes.availableAimModes.Contains(AimMode.AimedShot) && compFireModes.currentAimModeInt != AimMode.AimedShot)
                            {
                                compFireModes.currentAimModeInt = AimMode.AimedShot;
                                //    Log.Message("selected aimmode2: " + compFireModes.currentAimModeInt);
                            }
                        }
                        if (compFireModes.availableFireModes.Count > 1)
                        {
                            if (lengthToTarget <= (50 * rangemultiplier))
                            {
                                if (compFireModes.availableFireModes.Contains(FireMode.AutoFire) && compFireModes.currentFireModeInt != FireMode.AutoFire)
                                {
                                    compFireModes.currentFireModeInt = FireMode.AutoFire;
                                    //    Log.Message("selected firemode1: " + compFireModes.currentFireModeInt.ToString());
                                }
                            }
                            else
                            {
                                if (compFireModes.availableFireModes.Contains(FireMode.SingleFire) && compFireModes.currentFireModeInt != FireMode.SingleFire)
                                {
                                    compFireModes.currentFireModeInt = FireMode.SingleFire;
                                    //    Log.Message("selected firemode2: " + compFireModes.currentFireModeInt.ToString());
                                }
                            }
                        }
                    }
                }
            }

            if (p != null)
            {
                if ((Mathf.Abs(cachedShotRotation - shotRotation)) >= 15 && numShotsFired == 0)
                {
                    cachedShotRotation = shotRotation;
                    p.Drawer.Notify_WarmingCastAlongLine(shootLine, caster.Position);
                }
            }


            if (projectilePropsCE.pelletCount < 1)
            {
                Log.Error(EquipmentSource.LabelCap + " tried firing with pelletCount less than 1.");
                return(false);
            }
            ShiftVecReport report = ShiftVecReportFor(currentTarget);
            bool           pelletMechanicsOnly = false;

            for (int i = 0; i < projectilePropsCE.pelletCount; i++)
            {
                ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(Projectile, null);
                GenSpawn.Spawn(projectile, shootLine.Source, caster.Map);
                ShiftTarget(report, pelletMechanicsOnly);

                //New aiming algorithm
                projectile.canTargetSelf   = false;
                projectile.minCollisionSqr = (sourceLoc - currentTarget.Cell.ToIntVec2.ToVector2Shifted()).sqrMagnitude;
                projectile.intendedTarget  = currentTarget.Thing;
                projectile.Launch(
                    Shooter,            //Shooter instead of caster to give turret operators' records the damage/kills obtained
                    sourceLoc,
                    shotAngle,
                    shotRotation,
                    ShotHeight,
                    ShotSpeed,
                    EquipmentSource
                    );
                pelletMechanicsOnly = true;
                //    Log.Message("proj: " + projectile.ToString() + " shootlineSource: " + shootLine.Source.ToString() + " caster: " + caster.ToString() + " currentTarget.Thing: " + currentTarget.Thing.ToString() + " report: " + report.ToString());
                //    Log.Message("minCollisionSqr: " + projectile.minCollisionSqr.ToString() + " intendedTarget : " + projectile.intendedTarget.ToString() + " Shooter: " + Shooter.ToString()
                //        + " sourceLoc: " + sourceLoc.ToString() + " shotAngle: " + shotAngle.ToString() + " shotRotation: " + shotRotation.ToString() + " ShotHeight: " + ShotHeight.ToString()
                //        + " ShotHeight: " + ShotHeight.ToString() + "ShotSpeed: " + ShotSpeed.ToString() + " EquipmentSource: " + EquipmentSource.ToString());
            }
            pelletMechanicsOnly = false;
            this.numShotsFired++;
            return(true);
        }
Пример #13
0
        /// <summary>
        /// Fires a projectile using the new aiming system
        /// </summary>
        /// <returns>True for successful shot, false otherwise</returns>
        public override bool TryCastShot()
        {
            if (!TryFindCEShootLineFromTo(caster.Position, currentTarget, out var shootLine))
            {
                return(false);
            }
            if (projectilePropsCE.pelletCount < 1)
            {
                Log.Error(EquipmentSource.LabelCap + " tried firing with pelletCount less than 1.");
                return(false);
            }
            bool instant = false;

            float spreadDegrees = 0;
            float aperatureSize = 0;

            if (Projectile.projectile is ProjectilePropertiesCE pprop)
            {
                instant       = pprop.isInstant;
                spreadDegrees = (EquipmentSource?.GetStatValue(StatDef.Named("ShotSpread")) ?? 0) * pprop.spreadMult;
                aperatureSize = 0.03f;
            }

            ShiftVecReport report = ShiftVecReportFor(currentTarget);
            bool           pelletMechanicsOnly = false;

            for (int i = 0; i < projectilePropsCE.pelletCount; i++)
            {
                ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(Projectile, null);
                GenSpawn.Spawn(projectile, shootLine.Source, caster.Map);
                ShiftTarget(report, pelletMechanicsOnly, instant);

                //New aiming algorithm
                projectile.canTargetSelf = false;

                var targetDistance = (sourceLoc - currentTarget.Cell.ToIntVec2.ToVector2Shifted()).magnitude;
                projectile.minCollisionDistance = GetMinCollisionDistance(targetDistance);
                projectile.intendedTarget       = currentTarget;
                projectile.mount          = caster.Position.GetThingList(caster.Map).FirstOrDefault(t => t is Pawn && t != caster);
                projectile.AccuracyFactor = report.accuracyFactor * report.swayDegrees * ((numShotsFired + 1) * 0.75f);

                if (instant)
                {
                    var   shotHeight = ShotHeight;
                    float tsa        = AdjustShotHeight(caster, currentTarget, ref shotHeight);
                    projectile.RayCast(
                        Shooter,
                        verbProps,
                        sourceLoc,
                        shotAngle + tsa,
                        shotRotation,
                        shotHeight,
                        ShotSpeed,
                        spreadDegrees,
                        aperatureSize,
                        EquipmentSource);
                }
                else
                {
                    projectile.Launch(
                        Shooter,                  //Shooter instead of caster to give turret operators' records the damage/kills obtained
                        sourceLoc,
                        shotAngle,
                        shotRotation,
                        ShotHeight,
                        ShotSpeed,
                        EquipmentSource);
                }
                pelletMechanicsOnly = true;
            }

            /*
             * Notify the lighting tracker that shots fired with muzzle flash value of VerbPropsCE.muzzleFlashScale
             */
            LightingTracker.Notify_ShotsFiredAt(caster.Position, intensity: VerbPropsCE.muzzleFlashScale);

            pelletMechanicsOnly = false;
            numShotsFired++;
            if (CompAmmo != null && !CompAmmo.CanBeFiredNow)
            {
                CompAmmo?.TryStartReload();
            }
            if (CompReloadable != null)
            {
                CompReloadable.UsedOnce();
            }
            return(true);
        }
        /// <summary>
        /// Shifts the original target position in accordance with target leading, range estimation and weather/lighting effects
        /// </summary>
        protected virtual Vector3 ShiftTarget(ShiftVecReport report)
        {
            // ----------------------------------- STEP 0: Actual location

            Vector3 targetLoc = report.targetPawn != null?Vector3.Scale(report.targetPawn.DrawPos, new Vector3(1, 0, 1)) : report.target.Cell.ToVector3Shifted();

            Vector3 sourceLoc = this.CasterPawn != null?Vector3.Scale(this.CasterPawn.DrawPos, new Vector3(1, 0, 1)) : this.caster.Position.ToVector3Shifted();

            // ----------------------------------- STEP 1: Shift for visibility

            Vector2 circularShiftVec = report.GetRandCircularVec();
            Vector3 newTargetLoc     = targetLoc;

            newTargetLoc.x += circularShiftVec.x;
            newTargetLoc.z += circularShiftVec.y;

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

            // On first shot of burst do a range estimate
            if (this.estimatedTargDist < 0)
            {
                this.estimatedTargDist = report.GetRandDist();
            }
            newTargetLoc = sourceLoc + (newTargetLoc - sourceLoc).normalized * this.estimatedTargDist;

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

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

            Vector2 skewVec = new Vector2(0, 0);

            skewVec += this.GetSwayVec();
            skewVec += this.GetRecoilVec();

            // Height difference calculations for ShotAngle
            float heightDifference = 0;
            float targetableHeight = 0;

            // Projectiles with flyOverhead target the ground below the target and ignore cover
            if (!projectileDef.projectile.flyOverhead)
            {
                targetableHeight = CE_Utility.GetCollisionHeight(this.currentTarget.Thing);
                if (report.cover != null)
                {
                    targetableHeight += CE_Utility.GetCollisionHeight(report.cover);
                }
                heightDifference += targetableHeight * 0.5f;    //Optimal hit level is halfway
            }

            this.shotHeight = CE_Utility.GetCollisionHeight(this.caster);
            if (this.CasterPawn != null)
            {
                this.shotHeight *= shotHeightFactor;
            }
            heightDifference -= this.shotHeight;
            skewVec          += new Vector2(0, GetShotAngle(this.shotSpeed, (newTargetLoc - sourceLoc).magnitude, heightDifference) * (180 / (float)Math.PI));

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

            // Get shotvariation
            Vector2 spreadVec = report.GetRandSpreadVec();

            skewVec += spreadVec;

            // Skewing		-		Applied after the leading calculations to not screw them up
            float distanceTraveled = GetDistanceTraveled(this.shotSpeed, (float)(skewVec.y * (Math.PI / 180)), this.shotHeight);

            newTargetLoc = sourceLoc + ((newTargetLoc - sourceLoc).normalized * distanceTraveled);
            newTargetLoc = sourceLoc + (Quaternion.AngleAxis(skewVec.x, Vector3.up) * (newTargetLoc - sourceLoc));

            this.shotAngle = (float)(skewVec.y * (Math.PI / 180));

            return(newTargetLoc);
        }