protected virtual void Explode()
        {
            ExplosionCE explosion = GenSpawn.Spawn(CE_ThingDefOf.ExplosionCE, ExactPosition.ToIntVec3(), Map) as ExplosionCE;

            explosion.height     = ExactPosition.y;
            explosion.radius     = def.projectile.explosionRadius;
            explosion.damType    = def.projectile.damageDef;
            explosion.instigator = launcher;
            explosion.damAmount  = def.projectile.GetDamageAmount(1);
            explosion.weapon     = equipmentDef;
            explosion.projectile = def;
            explosion.preExplosionSpawnThingDef            = def.projectile.preExplosionSpawnThingDef;
            explosion.preExplosionSpawnChance              = def.projectile.preExplosionSpawnChance;
            explosion.preExplosionSpawnThingCount          = def.projectile.preExplosionSpawnThingCount;
            explosion.postExplosionSpawnThingDef           = def.projectile.postExplosionSpawnThingDef;
            explosion.postExplosionSpawnChance             = def.projectile.postExplosionSpawnChance;
            explosion.postExplosionSpawnThingCount         = def.projectile.postExplosionSpawnThingCount;
            explosion.applyDamageToExplosionCellsNeighbors = def.projectile.applyDamageToExplosionCellsNeighbors;
            explosion.chanceToStartFire = def.projectile.explosionChanceToStartFire;
            explosion.damageFalloff     = def.projectile.explosionDamageFalloff;
            explosion.StartExplosion(def.projectile.soundExplode);
            explosion.armorPenetration = explosion.damAmount * 0.1f;

            //This code was disabled because it didn't run under previous circumstances. Could be enabled if necessary

            /*
             * if (map != null && base.ExactPosition.ToIntVec3().IsValid)
             * {
             *  ThrowBigExplode(base.ExactPosition + Gen.RandomHorizontalVector(def.projectile.explosionRadius * 0.5f), base.Map, def.projectile.explosionRadius * 0.4f);
             * }
             */

            base.Impact(null); // base.Impact() handles this.Destroy() and comp.Explode()
        }
Beispiel #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;
            }

            if (!Props.fragments.NullOrEmpty())
            {
                var projCE        = parent as ProjectileCE;
                var edifice       = posIV.GetEdifice(map);
                var edificeHeight = edifice == null ? 0 : new CollisionVertical(edifice).Max;
                var height        = projCE != null?Mathf.Max(edificeHeight, pos.y) : edificeHeight;

                foreach (var fragment in Props.fragments)
                {
                    _monoDummy.GetComponent <MonoDummy>().StartCoroutine(FragRoutine(pos, map, height, instigator, fragment, Props.fragSpeedFactor));
                }
            }

            // 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;
                explosion.armorPenetration  = explosion.damAmount * 0.1f;
                explosion.damageFalloff     = Props.damageFalloff;
                explosion.chanceToStartFire = Props.chanceToStartFire;
                explosion.StartExplosion(Props.soundExplode ?? Props.explosionDamageDef.soundExplosion);
            }
        }
Beispiel #3
0
        public const float MaxExplosionScale = 10f;     //as if 1000 shells exploded

        public static void DoExplosion(IntVec3 center, Map map, float radius, DamageDef damType, Thing instigator, int damAmount = -1, float armorPenetration = -1f, SoundDef explosionSound = null, ThingDef weapon = null, ThingDef projectile = null, Thing intendedTarget = null, ThingDef postExplosionSpawnThingDef = null, float postExplosionSpawnChance = 0f, int postExplosionSpawnThingCount = 1, bool applyDamageToExplosionCellsNeighbors = false, ThingDef preExplosionSpawnThingDef = null, float preExplosionSpawnChance = 0f, int preExplosionSpawnThingCount = 1, float chanceToStartFire = 0f, bool damageFalloff = false, float?direction = null, List <Thing> ignoredThings = null,
                                       float height = 0f, float scaleFactor = 1f, bool destroyAfterwards = false, ThingWithComps explosionParentToDestroy = null)
        {
            // Allows destroyed things to be exploded with appropriate scaleFactor
            if (scaleFactor <= 0f)
            {
                scaleFactor = 1f;
            }
            else
            {
                scaleFactor = Mathf.Clamp(scaleFactor, MinExplosionScale, MaxExplosionScale);
            }

            if (map == null)
            {
                Log.Warning("CombatExtended :: Tried to do explosionCE in a null map.");
                return;
            }
            if (damAmount < 0)
            {
                damAmount        = damType.defaultDamage;
                armorPenetration = damType.defaultArmorPenetration;
                if (damAmount < 0)
                {
                    Log.ErrorOnce("CombatExtended :: Attempted to trigger an explosionCE without defined damage", 910948823);
                    damAmount = 1;
                }
            }

            explosionSound = explosionSound ?? damType.soundExplosion;

            if (explosionSound == null)
            {
                Log.Error("CombatExtended :: SoundDef was null for DamageDef " + damType.defName + " as well as instigator " + instigator.ThingID);
            }

            damAmount         = Mathf.RoundToInt(damAmount * scaleFactor);
            radius           *= scaleFactor;
            armorPenetration *= scaleFactor;

            ExplosionCE explosion      = GenSpawn.Spawn(CE_ThingDefOf.ExplosionCE, center, map) as ExplosionCE;
            IntVec3?    needLOSToCell  = null;
            IntVec3?    needLOSToCell2 = null;

            if (direction.HasValue)
            {
                CalculateNeededLOSToCells(center, map, direction.Value, out needLOSToCell, out needLOSToCell2);
            }
            explosion.height                               = height;
            explosion.radius                               = radius;
            explosion.damType                              = damType;
            explosion.instigator                           = instigator;
            explosion.damAmount                            = damAmount;
            explosion.armorPenetration                     = armorPenetration;
            explosion.weapon                               = weapon;
            explosion.projectile                           = projectile;
            explosion.intendedTarget                       = intendedTarget;
            explosion.preExplosionSpawnThingDef            = preExplosionSpawnThingDef;
            explosion.preExplosionSpawnChance              = preExplosionSpawnChance;
            explosion.preExplosionSpawnThingCount          = preExplosionSpawnThingCount;
            explosion.postExplosionSpawnThingDef           = postExplosionSpawnThingDef;
            explosion.postExplosionSpawnChance             = postExplosionSpawnChance;
            explosion.postExplosionSpawnThingCount         = postExplosionSpawnThingCount;
            explosion.applyDamageToExplosionCellsNeighbors = applyDamageToExplosionCellsNeighbors;
            explosion.chanceToStartFire                    = chanceToStartFire;
            explosion.damageFalloff                        = damageFalloff;
            explosion.needLOSToCell1                       = needLOSToCell;
            explosion.needLOSToCell2                       = needLOSToCell2;
            explosion.StartExplosionCE(explosionSound, ignoredThings);

            // Needed to allow CompExplosive to use stackCount
            if (destroyAfterwards && !explosionParentToDestroy.Destroyed)
            {
                explosionParentToDestroy?.Kill();
            }
        }
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.
        /// 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 #5
0
        public void StartExplosionCE(SoundDef explosionSound, List <Thing> ignoredThings)    //Removed cellsToAffect by Worker
        {
            if (Controller.settings.MergeExplosions)
            {
                ExplosionCE existingExplosion = Position.GetThingList(Map).FirstOrDefault(x => x.def == CE_ThingDefOf.ExplosionCE && x != this && !(x as ExplosionCE).toBeMerged) as ExplosionCE;

                if (existingExplosion == null)
                {
                    var i   = 1;
                    var num = GenRadial.NumCellsInRadius(MaxMergeRange);

                    while (i < num && existingExplosion == null)
                    {
                        IntVec3 c = Position + GenRadial.RadialPattern[i];
                        if (c.InBounds(Map))
                        {
                            existingExplosion = c.GetThingList(Map).FirstOrDefault(x => x.def == CE_ThingDefOf.ExplosionCE && x != this && !(x as ExplosionCE).toBeMerged) as ExplosionCE;
                        }
                        i++;
                    }
                }

                if (existingExplosion != null)
                {
                    if (MergeWith(existingExplosion, out var merged, out var nonMerged))
                    {
                        nonMerged.toBeMerged = true;
                        //Was possible to merge, destroy nonmerged (later) and start merged.
                        merged.RestartAfterMerge(explosionSound);
                        return;
                    }
                }
            }

            if (!Spawned)
            {
                Log.Error("Called StartExplosion() on unspawned thing.");
                return;
            }

            if (this.ignoredThings.NullOrEmpty())
            {
                this.ignoredThings = ignoredThings;
            }

            startTick = Find.TickManager.TicksGame;
            cellsToAffect.Clear();
            damagedThings.Clear();
            addedCellsAffectedOnlyByDamage.Clear();
            //this.cellsToAffect.AddRange(this.damType.Worker.ExplosionCellsToHit(this));
            cellsToAffect.AddRange(ExplosionCellsToHit);
            if (applyDamageToExplosionCellsNeighbors)
            {
                AddCellsNeighbors(cellsToAffect);
            }
            damType.Worker.ExplosionStart(this, cellsToAffect);
            PlayExplosionSound(explosionSound);
            FleckMaker.WaterSplash(Position.ToVector3Shifted(), Map, radius * 6f, 20f);
            cellsToAffect.Sort((IntVec3 a, IntVec3 b) => GetCellAffectTick(b).CompareTo(GetCellAffectTick(a)));
            RegionTraverser.BreadthFirstTraverse(Position, Map, (Region from, Region to) => true, delegate(Region x)
            {
                var list = x.ListerThings.ThingsInGroup(ThingRequestGroup.Pawn);
                for (var i = list.Count - 1; i >= 0; i--)
                {
                    ((Pawn)list[i]).mindState.Notify_Explosion(this);
                }
                return(false);
            }, 25, RegionType.Set_Passable);
        }
Beispiel #6
0
        // New method
        public virtual bool MergeWith(ExplosionCE other, out ExplosionCE merged, out ExplosionCE nonMerged)
        {
            merged    = null;
            nonMerged = null;

            //Log.Message("This: "+this.ThingID+", Other: "+other.ThingID);

            //Sanity check
            if (other == null)
            {
                return(false);
            }

            var startDiff = (other.startTick == 0 ? Find.TickManager.TicksGame : other.startTick)
                            - (startTick == 0 ? Find.TickManager.TicksGame : startTick);

            //Log.Message("StartDiff: " + startDiff);

            if (Mathf.Abs(startDiff) > MaxMergeTicks)
            {
                return(false);
            }

            //Log.Message("DF: " + damageFalloff + ", " + other.damageFalloff);

            if (other.damageFalloff != damageFalloff)
            {
                return(false);
            }

            //Log.Message("DT: " + damType.defName + ", " + other.damType.defName);

            if (other.damType != damType)
            {
                return(false);
            }

            //Log.Message("Sign: " + Mathf.Sign(height - CollisionVertical.WallCollisionHeight) + ", " + Mathf.Sign(other.height - CollisionVertical.WallCollisionHeight));

            if (Mathf.Sign(other.height - CollisionVertical.WallCollisionHeight) != Mathf.Sign(height - CollisionVertical.WallCollisionHeight))
            {
                return(false);
            }

            //Log.Message("PreExp: " + (preExplosionSpawnThingDef != null ? preExplosionSpawnThingDef.defName : "null") + ", " + (other.preExplosionSpawnThingDef != null ? other.preExplosionSpawnThingDef.defName : "null"));

            if (preExplosionSpawnThingDef != null && other.preExplosionSpawnThingDef != null && other.preExplosionSpawnThingDef != preExplosionSpawnThingDef)
            {
                return(false);
            }

            //Log.Message("PostExp: " + (postExplosionSpawnThingDef != null ? postExplosionSpawnThingDef.defName : "null") + ", " + (other.postExplosionSpawnThingDef != null ? other.postExplosionSpawnThingDef.defName : "null"));

            if (postExplosionSpawnThingDef != null && other.postExplosionSpawnThingDef != null && other.postExplosionSpawnThingDef != postExplosionSpawnThingDef)
            {
                return(false);
            }

            Thing newInstigator = null;

            //Log.Message("Instig: " + (instigator != null ? instigator.ThingID : "null") + ", " + (other.instigator != null ? other.instigator.ThingID : "null"));

            //Instigator "equal-enough"
            if (other.instigator != null && instigator != null)
            {
                //If both were hostile action..
                if (other.intendedTarget != null && intendedTarget != null &&
                    other.intendedTarget.HostileTo(other.instigator) &&
                    intendedTarget.HostileTo(instigator))
                {
                    //If both instigators had different factions, the explosions had different intentions -- cannot merge
                    if (instigator.Faction != null && other.instigator.Faction != null && instigator.Faction != other.instigator.Faction)
                    {
                        return(false);
                    }
                }
                else
                {
                    if (other.instigator != instigator)
                    {
                        //Impossible to distinguish for a combat log which pawn initiated the explosion
                        if (instigator is Pawn && other.instigator is Pawn)
                        {
                            return(false);
                        }
                        else
                        {
                            if (instigator is Pawn)
                            {
                                newInstigator = instigator;
                            }
                            else if (other.instigator is Pawn)
                            {
                                newInstigator = other.instigator;
                            }

                            if (instigator is AmmoThing || other.instigator is AmmoThing)
                            {
                                newInstigator = instigator is AmmoThing ? other.instigator : instigator;
                            }
                            else
                            {
                                newInstigator = Rand.Value < 0.5f ? instigator : other.instigator;
                            }
                        }
                    }
                }
            }

            //Log.Message("Wpn: " + (weapon != null ? weapon.defName : "null") + ", " + (other.weapon != null ? other.weapon.defName : "null"));

            //Might be problematic
            if (other.weapon != null && weapon != null && other.weapon != weapon)
            {
                return(false);
            }

            //Log.Message("Prj: " + (projectile != null ? projectile.defName : "null") + ", " + (other.projectile != null ? other.projectile.defName : "null"));

            //Might be problematic
            if (other.projectile != null && projectile != null && other.projectile != projectile)
            {
                return(false);
            }

            //Log.Message("LOS1: " + needLOSToCell1.ToString() + ", " + other.needLOSToCell1.ToString());

            if (other.needLOSToCell1 != needLOSToCell1)
            {
                return(false);
            }

            //Log.Message("LOS2: " + needLOSToCell2.ToString() + ", " + other.needLOSToCell2.ToString());

            if (other.needLOSToCell2 != needLOSToCell2)
            {
                return(false);
            }

            //Crucial matches
            merged    = startDiff <= 0 ? this : other;
            nonMerged = startDiff <= 0 ? other : this;

            //Log.Message("this: "+this.ThingID + " merged: "+merged.ThingID + " other: "+other.ThingID + " nonmerged: "+nonMerged.ThingID);

            merged.instigator = newInstigator;

            //Combine shared ignored things
            var newIgnoredThings = new HashSet <Thing>();

            if (ignoredThings != null && other.ignoredThings != null)
            {
                newIgnoredThings.AddRange(ignoredThings.Where(x => other.ignoredThings.Contains(x)));
            }

            //Add instigators if necessary
            if (ignoredThings != null && ignoredThings.Contains(instigator))
            {
                newIgnoredThings.Add(instigator);
            }
            if (other.ignoredThings != null && other.ignoredThings.Contains(other.instigator))
            {
                newIgnoredThings.Add(other.instigator);
            }

            merged.ignoredThings = newIgnoredThings.ToList();

            //Combine chances such that the same spread of things is observed, while the average is retained to the best of our ability using integer values only
            merged.chanceToStartFire           = 1 - (1 - chanceToStartFire) * (1 - other.chanceToStartFire);
            merged.preExplosionSpawnThingCount = Mathf.RoundToInt((preExplosionSpawnThingCount * preExplosionSpawnChance
                                                                   + other.preExplosionSpawnThingCount * other.preExplosionSpawnChance) / (1 - (1 - preExplosionSpawnChance) * (1 - other.preExplosionSpawnChance)));
            merged.preExplosionSpawnChance      = 1 - (1 - preExplosionSpawnChance) * (1 - other.preExplosionSpawnChance);
            merged.postExplosionSpawnThingCount = Mathf.RoundToInt((postExplosionSpawnThingCount * postExplosionSpawnChance
                                                                    + other.postExplosionSpawnThingCount * other.postExplosionSpawnChance) / (1 - (1 - postExplosionSpawnChance) * (1 - other.postExplosionSpawnChance)));
            merged.postExplosionSpawnChance = 1 - (1 - postExplosionSpawnChance) * (1 - other.postExplosionSpawnChance);

            //Linearly combine damage, since that's how it would be if both explosions ran
            merged.armorPenetration = Mathf.Max(damAmount * PressurePerDamage, armorPenetration)
                                      + Mathf.Max(other.damAmount * PressurePerDamage, other.armorPenetration);
            merged.damAmount = damAmount + other.damAmount;

            if (!merged.applyDamageToExplosionCellsNeighbors && nonMerged.applyDamageToExplosionCellsNeighbors)
            {
                merged.applyDamageToExplosionCellsNeighbors = true;
                merged.radiusChange = true;
            }

            if (radius != other.radius && merged.radius < nonMerged.radius)
            {
                merged.radius       = Mathf.Max(radius, other.radius);
                merged.radiusChange = true;
            }

            return(true);
        }