private bool TryLaunchCookOffProjectile()
        {
            if (AmmoDef.cookOffProjectile == null)
            {
                return(false);
            }
            ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(AmmoDef.cookOffProjectile);

            GenSpawn.Spawn(projectile, Position, Map);

            // Launch in random direction
            projectile.canTargetSelf   = true;
            projectile.minCollisionSqr = 0f;
            projectile.Launch(this,
                              new Vector2(DrawPos.x, DrawPos.z),
                              UnityEngine.Random.Range(0, Mathf.PI / 2f),
                              UnityEngine.Random.Range(0, 360),
                              0.1f,
                              AmmoDef.cookOffProjectile.projectile.speed * AmmoDef.cookOffSpeed);
            if (AmmoDef.cookOffFlashScale > 0.01)
            {
                MoteMaker.MakeStaticMote(Position, Map, ThingDefOf.Mote_ShotFlash, AmmoDef.cookOffFlashScale);
            }
            if (AmmoDef.cookOffSound != null)
            {
                AmmoDef.cookOffSound.PlayOneShot(new TargetInfo(Position, Map));
            }
            if (AmmoDef.cookOffTailSound != null)
            {
                AmmoDef.cookOffTailSound.PlayOneShotOnCamera();
            }
            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 (!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);
        }
Beispiel #3
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);
        }
Beispiel #4
0
        /// <summary>
        /// Produces a secondary explosion on impact using the explosion values from the projectile's projectile def. Requires the projectile's launcher to be passed on due to protection level,
        /// only works when parent can be cast as ProjectileCE. Intended use is for HEAT and similar weapons that spawn secondary explosions while also penetrating, NOT explosive ammo of
        /// anti-materiel rifles as the explosion just spawns on top of the pawn, not inside the hit body part.
        ///
        /// Additionally handles fragmentation effects if defined.
        /// </summary>
        /// <param name="instigator">Launcher of the projectile calling the method</param>
        public virtual void Explode(Thing instigator, IntVec3 pos, Map map)
        {
            if (map == null)
            {
                Log.Warning("Tried to do explodeCE in a null map.");
                return;
            }
            // Regular explosion stuff
            if (Props.explosionRadius > 0 && Props.explosionDamage > 0 && parent.def != null && GenGrid.InBounds(pos, map))
            {
                GenExplosion.DoExplosion
                    (pos,
                    map,
                    Props.explosionRadius,
                    Props.explosionDamageDef,
                    instigator,
                    Props.soundExplode == null ? Props.explosionDamageDef.soundExplosion : Props.soundExplode,
                    parent.def,
                    null,
                    Props.postExplosionSpawnThingDef           = null,
                    Props.postExplosionSpawnChance             = 0f,
                    Props.postExplosionSpawnThingCount         = 1,
                    Props.applyDamageToExplosionCellsNeighbors = false,
                    Props.preExplosionSpawnThingDef            = null,
                    Props.explosionSpawnChance = 0,
                    Props.preExplosionSpawnThingCount);
            }
            // Fragmentation stuff
            if (!Props.fragments.NullOrEmpty() && GenGrid.InBounds(pos, map))
            {
                if (Props.fragRange <= 0)
                {
                    Log.Error(parent.LabelCap + " has fragments but no fragRange");
                }

                else
                {
                    Vector3 exactOrigin = new Vector3(0, 0, 0);
                    exactOrigin.x = parent.DrawPos.x;
                    exactOrigin.z = parent.DrawPos.z;
                    foreach (ThingCountClass fragment in Props.fragments)
                    {
                        for (int i = 0; i < fragment.count; i++)
                        {
                            ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(fragment.thingDef, null);
                            projectile.canFreeIntercept = true;
                            Vector3         exactTarget = exactOrigin + (new Vector3(1, 0, 1) * UnityEngine.Random.Range(0, Props.fragRange)).RotatedBy(UnityEngine.Random.Range(0, 360));
                            LocalTargetInfo targetCell  = exactTarget.ToIntVec3();
                            GenSpawn.Spawn(projectile, parent.Position, map);
                            projectile.Launch(instigator, exactOrigin, targetCell, exactTarget, null);
                        }
                    }
                }
            }
        }
Beispiel #5
0
        private bool TryLaunchCookOffProjectile()
        {
            if (AmmoDef.cookOffProjectile == null)
            {
                return(false);
            }

            Fire fire = Position.GetThingList(Map).Find(f => f.Spawned && f is Fire) as Fire;

            if (fire != null && fire.fireSize < 0.4)
            {
                return(false);
            }

            // Spawn projectile if enabled
            if (!Controller.settings.RealisticCookOff)
            {
                ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(AmmoDef.cookOffProjectile);
                GenSpawn.Spawn(projectile, Position, Map);

                // Launch in random direction
                projectile.canTargetSelf   = true;
                projectile.minCollisionSqr = 0f;
                projectile.logMisses       = false;
                projectile.Launch(this,
                                  new Vector2(DrawPos.x, DrawPos.z),
                                  UnityEngine.Random.Range(0, Mathf.PI / 2f),
                                  UnityEngine.Random.Range(0, 360),
                                  0.1f,
                                  AmmoDef.cookOffProjectile.projectile.speed * AmmoDef.cookOffSpeed,
                                  this);
            }
            // Create sound and flash effects
            if (AmmoDef.cookOffFlashScale > 0.01)
            {
                MoteMaker.MakeStaticMote(Position, Map, ThingDefOf.Mote_ShotFlash, AmmoDef.cookOffFlashScale);
            }
            if (AmmoDef.cookOffSound != null)
            {
                AmmoDef.cookOffSound.PlayOneShot(new TargetInfo(Position, Map));
            }
            if (AmmoDef.cookOffTailSound != null)
            {
                AmmoDef.cookOffTailSound.PlayOneShotOnCamera();
            }

            return(true);
        }
Beispiel #6
0
        private bool TryLaunchCookOffProjectile()
        {
            if (AmmoDef == null || AmmoDef.cookOffProjectile == null || Find.Maps.IndexOf(Map) < 0)
            {
                return(false);
            }

            // Spawn projectile if enabled
            if (!Controller.settings.RealisticCookOff)
            {
                ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(AmmoDef.cookOffProjectile);
                GenSpawn.Spawn(projectile, PositionHeld, MapHeld);

                // Launch in random direction
                projectile.canTargetSelf        = true;
                projectile.minCollisionDistance = 0f;
                projectile.logMisses            = false;
                projectile.Launch(this,
                                  new Vector2(DrawPos.x, DrawPos.z),
                                  Mathf.Acos(2 * UnityEngine.Random.Range(0.5f, 1f) - 1),
                                  UnityEngine.Random.Range(0, 360),
                                  0.1f,
                                  AmmoDef.cookOffProjectile.projectile.speed * AmmoDef.cookOffSpeed,
                                  this);
            }
            // Create sound and flash effects
            if (AmmoDef.cookOffFlashScale > 0.01)
            {
                FleckMaker.Static(Position, Map, FleckDefOf.ShotFlash, AmmoDef.cookOffFlashScale);
            }
            if (AmmoDef.cookOffSound != null)
            {
                AmmoDef.cookOffSound.PlayOneShot(new TargetInfo(Position, Map));
            }
            if (AmmoDef.cookOffTailSound != null)
            {
                AmmoDef.cookOffTailSound.PlayOneShotOnCamera();
            }

            return(true);
        }
        /// <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);
        }
Beispiel #9
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);
        }
Beispiel #11
0
        /// <summary>
        /// Produces a secondary explosion on impact using the explosion values from the projectile's projectile def. Requires the projectile's launcher to be passed on due to protection level.
        /// Intended use is for HEAT and similar weapons that spawn secondary explosions while also penetrating, NOT explosive ammo of anti-materiel rifles as the explosion just spawns
        /// on top of the pawn, not inside the hit body part.
        ///
        /// Additionally handles fragmentation effects if defined.
        /// </summary>
        /// <param name="instigator">Launcher of the projectile calling the method</param>
        public virtual void Explode(Thing instigator, Vector3 pos, Map map, float scaleFactor = 1)
        {
            var posIV = pos.ToIntVec3();

            if (map == null)
            {
                Log.Warning("Tried to do explodeCE in a null map.");
                return;
            }
            if (!posIV.InBounds(map))
            {
                Log.Warning("Tried to explodeCE out of bounds");
                return;
            }

            var projCE = parent as ProjectileCE;

            #region Fragmentation
            if (!Props.fragments.NullOrEmpty())
            {
                float   edificeHeight = (new CollisionVertical(posIV.GetEdifice(map))).Max;
                Vector2 exactOrigin   = new Vector2(pos.x, pos.z);
                float   height;

                //Fragments fly from a 0 to 45 degree angle away from the explosion
                var range = new FloatRange(0, Mathf.PI / 8f);

                if (projCE != null)
                {
                    height = Mathf.Max(edificeHeight, pos.y);
                    if (edificeHeight < height)
                    {
                        //If the projectile exploded above the ground, they can fly 45 degree away at the bottom as well
                        range.min = -Mathf.PI / 8f;
                    }
                    // TODO : Check for hitting the bottom or top of a roof
                }
                else
                {
                    //Height is not tracked on non-CE projectiles, so we assume this one's on top of the edifice
                    height = edificeHeight;
                }

                foreach (ThingCountClass fragment in Props.fragments)
                {
                    for (int i = 0; i < fragment.count; i++)
                    {
                        ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(fragment.thingDef, null);
                        GenSpawn.Spawn(projectile, posIV, map);

                        projectile.canTargetSelf   = true;
                        projectile.minCollisionSqr = 1f;
                        //TODO : Don't hardcode at FragmentShadowChance, make XML-modifiable
                        projectile.castShadow = (UnityEngine.Random.value < FragmentShadowChance);
                        projectile.logMisses  = false;
                        projectile.Launch(
                            instigator,
                            exactOrigin,
                            range.RandomInRange,
                            UnityEngine.Random.Range(0, 360),
                            height,
                            Props.fragSpeedFactor * projectile.def.projectile.speed,
                            projCE
                            );
                    }
                }
            }
            #endregion

            // Regular explosion stuff
            if (Props.explosionRadius > 0 && Props.explosionDamage > 0 && parent.def != null && GenGrid.InBounds(posIV, map))
            {
                // Copy-paste from GenExplosion
                ExplosionCE explosion = GenSpawn.Spawn(CE_ThingDefOf.ExplosionCE, posIV, map) as ExplosionCE;
                explosion.height     = pos.y;
                explosion.radius     = Props.explosionRadius * scaleFactor;
                explosion.damType    = Props.explosionDamageDef;
                explosion.instigator = instigator;
                explosion.damAmount  = GenMath.RoundRandom(Props.explosionDamage * scaleFactor);
                explosion.weapon     = null;
                explosion.projectile = parent.def;
                explosion.preExplosionSpawnThingDef            = Props.preExplosionSpawnThingDef;
                explosion.preExplosionSpawnChance              = Props.preExplosionSpawnChance;
                explosion.preExplosionSpawnThingCount          = Props.preExplosionSpawnThingCount;
                explosion.postExplosionSpawnThingDef           = Props.postExplosionSpawnThingDef;
                explosion.postExplosionSpawnChance             = Props.postExplosionSpawnChance;
                explosion.postExplosionSpawnThingCount         = Props.postExplosionSpawnThingCount;
                explosion.applyDamageToExplosionCellsNeighbors = Props.applyDamageToExplosionCellsNeighbors;

                // TODO: for some reason projectile goes to null
                if (parent.def.projectile != null)
                {
                    explosion.chanceToStartFire      = parent.def.projectile.explosionChanceToStartFire;
                    explosion.dealMoreDamageAtCenter = parent.def.projectile.explosionDealMoreDamageAtCenter;
                }
                explosion.StartExplosion(Props.explosionDamageDef.soundExplosion);
            }
        }
Beispiel #12
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>
        /// Produces a secondary explosion on impact using the explosion values from the projectile's projectile def. Requires the projectile's launcher to be passed on due to protection level.
        /// Intended use is for HEAT and similar weapons that spawn secondary explosions while also penetrating, NOT explosive ammo of anti-materiel rifles as the explosion just spawns
        /// on top of the pawn, not inside the hit body part.
        ///
        /// Additionally handles fragmentation effects if defined.
        /// </summary>
        /// <param name="instigator">Launcher of the projectile calling the method</param>
        public virtual void Explode(Thing instigator, IntVec3 pos, Map map, float scaleFactor = 1)
        {
            if (map == null)
            {
                Log.Warning("Tried to do explodeCE in a null map.");
                return;
            }
            if (!pos.InBounds(map))
            {
                Log.Warning("Tried to explodeCE out of bounds");
                return;
            }

            // Fragmentation stuff
            if (!Props.fragments.NullOrEmpty() && GenGrid.InBounds(pos, map))
            {
                Vector2 exactOrigin = new Vector2(parent.DrawPos.x, parent.DrawPos.z);
                float   height      = (new CollisionVertical(pos.GetEdifice(map))).Max;

                foreach (ThingCountClass fragment in Props.fragments)
                {
                    for (int i = 0; i < fragment.count; i++)
                    {
                        ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(fragment.thingDef, null);
                        GenSpawn.Spawn(projectile, pos, map);

                        var projCE = parent as ProjectileCE;
                        if (projCE != null)
                        {
                            height = Mathf.Max(height, projCE.Height);
                        }

                        projectile.canTargetSelf   = true;
                        projectile.minCollisionSqr = 1f;
                        projectile.Launch(instigator, exactOrigin, UnityEngine.Random.Range(0, Mathf.PI / 8f), UnityEngine.Random.Range(0, 360), height, Props.fragSpeedFactor * projectile.def.projectile.speed);
                    }
                }
            }
            // Regular explosion stuff
            if (Props.explosionRadius > 0 && Props.explosionDamage > 0 && parent.def != null && GenGrid.InBounds(pos, map))
            {
                // Can't use GenExplosion because it no longer allows setting damage amount

                // Copy-paste from GenExplosion
                Explosion explosion = new Explosion();
                explosion.position   = pos;
                explosion.radius     = Props.explosionRadius * scaleFactor;
                explosion.damType    = Props.explosionDamageDef;
                explosion.instigator = instigator;
                explosion.damAmount  = GenMath.RoundRandom(Props.explosionDamage * scaleFactor);
                explosion.weaponGear = null;
                explosion.preExplosionSpawnThingDef            = Props.preExplosionSpawnThingDef;
                explosion.preExplosionSpawnChance              = Props.explosionSpawnChance;
                explosion.preExplosionSpawnThingCount          = Props.preExplosionSpawnThingCount;
                explosion.postExplosionSpawnThingDef           = Props.postExplosionSpawnThingDef;
                explosion.postExplosionSpawnChance             = Props.postExplosionSpawnChance;
                explosion.postExplosionSpawnThingCount         = Props.postExplosionSpawnThingCount;
                explosion.applyDamageToExplosionCellsNeighbors = Props.applyDamageToExplosionCellsNeighbors;
                map.GetComponent <ExplosionManager>().StartExplosion(explosion, Props.soundExplode ?? Props.explosionDamageDef.soundExplosion);
            }
        }
        /// <summary>
        /// Produces a secondary explosion on impact using the explosion values from the projectile's projectile def. Requires the projectile's launcher to be passed on due to protection level.
        /// Intended use is for HEAT and similar weapons that spawn secondary explosions while also penetrating, NOT explosive ammo of anti-materiel rifles as the explosion just spawns
        /// on top of the pawn, not inside the hit body part.
        ///
        /// Additionally handles fragmentation effects if defined.
        /// </summary>
        /// <param name="instigator">Launcher of the projectile calling the method</param>
        public virtual void Explode(Thing instigator, Vector3 pos, Map map, float scaleFactor = 1)
        {
            var posIV = pos.ToIntVec3();

            if (map == null)
            {
                Log.Warning("Tried to do explodeCE in a null map.");
                return;
            }
            if (!posIV.InBounds(map))
            {
                Log.Warning("Tried to explodeCE out of bounds");
                return;
            }

            // Fragmentation stuff
            if (!Props.fragments.NullOrEmpty() && GenGrid.InBounds(posIV, map))
            {
                float   edificeHeight = (new CollisionVertical(posIV.GetEdifice(map))).Max;
                Vector2 exactOrigin   = new Vector2(pos.x, pos.z);
                float   height;

                //Fragments fly from a 0 to 45 degree angle away from the explosion
                var range = new FloatRange(0, Mathf.PI / 8f);

                var projCE = parent as ProjectileCE;

                if (projCE != null)
                {
                    height = Mathf.Max(edificeHeight, pos.y);
                    if (edificeHeight < height)
                    {
                        //If the projectile exploded above the ground, they can fly 45 degree away at the bottom as well
                        range.min = -Mathf.PI / 8f;
                    }
                    // TODO : Check for hitting the bottom or top of a roof
                }
                else
                {
                    height = edificeHeight;
                }

                foreach (ThingCountClass fragment in Props.fragments)
                {
                    for (int i = 0; i < fragment.count; i++)
                    {
                        ProjectileCE projectile = (ProjectileCE)ThingMaker.MakeThing(fragment.thingDef, null);
                        GenSpawn.Spawn(projectile, posIV, map);

                        projectile.canTargetSelf   = true;
                        projectile.minCollisionSqr = 1f;
                        //TODO : Don't hardcode at FragmentShadowChance, make XML-modifiable
                        projectile.castShadow = (UnityEngine.Random.value < FragmentShadowChance);
                        projectile.Launch(instigator, exactOrigin, range.RandomInRange, UnityEngine.Random.Range(0, 360), height, Props.fragSpeedFactor * projectile.def.projectile.speed);
                    }
                }
            }
            // Regular explosion stuff
            if (Props.explosionRadius > 0 && Props.explosionDamage > 0 && parent.def != null && GenGrid.InBounds(posIV, map))
            {
                // Can't use GenExplosion because it no longer allows setting damage amount

                // Copy-paste from GenExplosion
                Explosion explosion = new Explosion();
                explosion.position   = posIV;
                explosion.radius     = Props.explosionRadius * scaleFactor;
                explosion.damType    = Props.explosionDamageDef;
                explosion.instigator = instigator;
                explosion.damAmount  = GenMath.RoundRandom(Props.explosionDamage * scaleFactor);
                explosion.weaponGear = null;
                explosion.preExplosionSpawnThingDef            = Props.preExplosionSpawnThingDef;
                explosion.preExplosionSpawnChance              = Props.explosionSpawnChance;
                explosion.preExplosionSpawnThingCount          = Props.preExplosionSpawnThingCount;
                explosion.postExplosionSpawnThingDef           = Props.postExplosionSpawnThingDef;
                explosion.postExplosionSpawnChance             = Props.postExplosionSpawnChance;
                explosion.postExplosionSpawnThingCount         = Props.postExplosionSpawnThingCount;
                explosion.applyDamageToExplosionCellsNeighbors = Props.applyDamageToExplosionCellsNeighbors;
                map.GetComponent <ExplosionManager>().StartExplosion(explosion, Props.soundExplode ?? Props.explosionDamageDef.soundExplosion);
            }
        }