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

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

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

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

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

                // Apply secondary to non-pawns (pawn secondary damage is handled in the damage worker)
                var projectilePropsCE = def.projectile as ProjectilePropertiesCE;
                if (!(hitThing is Pawn) && projectilePropsCE != null && !projectilePropsCE.secondaryDamage.NullOrEmpty())
                {
                    foreach (SecondaryDamage cur in projectilePropsCE.secondaryDamage)
                    {
                        if (hitThing.Destroyed)
                        {
                            break;
                        }
                        var secDinfo = new DamageInfo(
                            cur.def,
                            cur.amount,
                            ExactRotation.eulerAngles.y,
                            launcher,
                            null,
                            def);
                        hitThing.TakeDamage(secDinfo);
                    }
                }
            }
            else
            {
                SoundDefOf.BulletImpactGround.PlayOneShot(new TargetInfo(base.Position, map, false));
                MoteMaker.MakeStaticMote(ExactPosition, map, ThingDefOf.Mote_ShotHit_Dirt, 1f);
            }
        }
        /// <summary>
        /// Calculates armor for penetrating damage types (Blunt, Sharp). Applies damage reduction based on armor penetration to armor ratio and calculates damage accordingly, with the difference being applied to the armor Thing. Also calculates whether a Sharp attack is deflected.
        /// </summary>
        /// <param name="def">The DamageDef of the attack</param>
        /// <param name="armorAmount">The amount of armor to apply</param>
        /// <param name="penAmount">How much penetration the attack still has</param>
        /// <param name="dmgAmount">The pre-armor amount of damage</param>
        /// <param name="armor">The armor apparel</param>
        /// <returns>False if the attack is deflected, true otherwise</returns>
        private static bool TryPenetrateArmor(DamageDef def, float armorAmount, ref float penAmount, ref float dmgAmount, Thing armor = null)
        {
            // Calculate deflection
            bool  isSharpDmg = def.armorCategory == DamageArmorCategoryDefOf.Sharp;
            float rand       = UnityEngine.Random.Range(penAmount - PenetrationRandVariation, penAmount + PenetrationRandVariation);
            bool  deflected  = isSharpDmg && armorAmount > rand;

            // Apply damage reduction
            float dmgMult = 1;
            DamageDefExtensionCE defCE = def.GetModExtension <DamageDefExtensionCE>() ?? new DamageDefExtensionCE();

            if (deflected && defCE != null && defCE.noDamageOnDeflect)
            {
                dmgMult = 0;
            }
            else
            {
                dmgMult = dmgMultCurve.Evaluate(penAmount / armorAmount);
            }
            float newDmgAmount = dmgAmount * dmgMult;
            float newPenAmount = penAmount * dmgMult;

            // Apply damage to armor
            if (armor != null)
            {
                bool isSoftArmor = armor.Stuff != null && armor.Stuff.stuffProps.categories.Any(s => softStuffs.Contains(s));

                // do not destroy thing if it already heavy damaged and not able to deflect damage.
                float armorhealth = ((float)armor.HitPoints / (float)armor.MaxHitPoints);
                if (deflected || (!deflected && armorhealth > 0.15f))
                {
                    if (isSoftArmor)
                    {
                        // Soft armor takes absorbed damage from sharp and no damage from blunt
                        if (isSharpDmg)
                        {
                            float armorDamage = Mathf.Max(dmgAmount * SoftArmorMinDamageFactor, dmgAmount - newDmgAmount);
                            armor.TakeDamage(new DamageInfo(def, Mathf.CeilToInt(armorDamage)));
                        }
                    }
                    else
                    {
                        // Hard armor takes damage as reduced by damage resistance and can be almost impervious to low-penetration attacks
                        float armorDamage = Mathf.Max(1, newDmgAmount);
                        armor.TakeDamage(new DamageInfo(def, Mathf.CeilToInt(armorDamage)));
                    }
                }
            }

            dmgAmount = Mathf.Max(0, newDmgAmount);
            penAmount = Mathf.Max(0, newPenAmount);
            return(!deflected);
        }
Example #3
0
        protected override void Impact(Thing hitThing)
        {
            Map map = base.Map;

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

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

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

                // Apply primary damage
                hitThing.TakeDamage(dinfo);
            }
            else
            {
                SoundDefOf.BulletImpactGround.PlayOneShot(new TargetInfo(base.Position, map, false));
                MoteMaker.MakeStaticMote(ExactPosition, map, ThingDefOf.Mote_ShotHit_Dirt, 1f);
            }
        }
Example #4
0
        protected override void Impact(Thing hitThing)
        {
            Map map = base.Map;
            BattleLogEntry_RangedImpact logEntry = null;

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

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

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

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

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

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

                //Only display a dirt hit for projectiles with a dropshadow
                if (base.castShadow)
                {
                    MoteMaker.MakeStaticMote(ExactPosition, map, ThingDefOf.Mote_ShotHit_Dirt, 1f);
                }
            }
            base.Impact(hitThing);
        }
Example #5
0
        protected override void Impact(Thing hitThing)
        {
            bool cookOff = (launcher is AmmoThing);

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

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

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

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

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

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

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

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

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

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