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); }
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); } }
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); }
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); }