예제 #1
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 (half of a circle) to 45 (3/4 of a circle) degree angle away from the explosion
                var range = new FloatRange(0.5f, 0.75f);

                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 (1/4 of a circle) at the bottom as well
                        range.min = 0.25f;
                    }
                    // 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 (ThingDefCountClass 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,
                            Mathf.Acos(2 * range.RandomInRange - 1),
                            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.damageFalloff     = parent.def.projectile.explosionDamageFalloff;
                }
                explosion.StartExplosion(Props.explosionDamageDef.soundExplosion);
            }
        }
예제 #2
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;
            }

            // 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);
            }
        }
        /// <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);
            }
            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 = Mathf.Min(1.5f, targetDistance * 0.75f);
                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);
                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;
            }
            /// 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);
        }