/// <summary> /// Determines the armor penetration value of a given dinfo. Checks WeaponGear to see if it is a projectile, melee weapon or pawn and tries to retrieve the penetration value accordingly. /// </summary> /// <param name="dinfo">DamageInfo to determine penetration for</param> /// <returns>Armor penetration value for attack used, 0 if it can't be determined</returns> private static float GetPenetrationValue(DamageInfo dinfo) { if (dinfo.Def.isExplosive) { return(dinfo.Amount * 0.1f); // Explosions have 10% of their damage as penetration } if (dinfo.WeaponGear != null) { // Case 1: projectile attack ProjectilePropertiesCE projectileProps = dinfo.WeaponGear.projectile as ProjectilePropertiesCE; if (projectileProps != null) { return(projectileProps.armorPenetration); } // Case 2: melee attack Pawn instigatorPawn = dinfo.Instigator as Pawn; if (instigatorPawn != null) { // Pawn is using melee weapon if (dinfo.WeaponGear.IsMeleeWeapon) { if (instigatorPawn.equipment == null || instigatorPawn.equipment.Primary == null || instigatorPawn.equipment.Primary.def != dinfo.WeaponGear) { Log.Error("CE tried getting armor penetration from melee weapon " + dinfo.WeaponGear.defName + " but instigator " + dinfo.Instigator.ToString() + " equipment does not match"); return(0); } return(instigatorPawn.equipment.Primary.GetStatValue(CE_StatDefOf.MeleeWeapon_Penetration)); } // Pawn is using body parts if (instigatorPawn.def == dinfo.WeaponGear) { // Pawn is augmented if (dinfo.WeaponLinkedHediff != null) { HediffCompProperties_VerbGiver compProps = dinfo.WeaponLinkedHediff.CompPropsFor(typeof(HediffComp_VerbGiver)) as HediffCompProperties_VerbGiver; if (compProps != null) { VerbPropertiesCE verbProps = compProps.verbs.FirstOrDefault(v => v as VerbPropertiesCE != null) as VerbPropertiesCE; if (verbProps != null) { return(verbProps.meleeArmorPenetration); } } return(0); } // Regular pawn melee if (dinfo.WeaponBodyPartGroup != null && instigatorPawn.verbTracker != null && !instigatorPawn.verbTracker.AllVerbs.NullOrEmpty()) { Verb verb = instigatorPawn.verbTracker.AllVerbs.FirstOrDefault(v => v.verbProps.linkedBodyPartsGroup == dinfo.WeaponBodyPartGroup); if (verb == null) { Log.Error("CE could not find matching verb on Pawn " + instigatorPawn.ToString() + " for BodyPartGroup " + dinfo.WeaponBodyPartGroup.ToString()); return(0); } VerbPropertiesCE verbProps = verb.verbProps as VerbPropertiesCE; if (verbProps != null) { return(verbProps.meleeArmorPenetration); } } } } } #if DEBUG Log.Warning("CE could not determine armor penetration, defaulting"); #endif return(9999); // Really high default value so vanilla damage sources such as GiveInjuriesToKill always penetrate }
/// <summary> /// Determines the armor penetration value of a given dinfo. Attempts to extract the tool/verb from the damage info. /// </summary> /// <param name="dinfo">DamageInfo to determine penetration for</param> /// <returns>Armor penetration value for attack used, 0 if it can't be determined</returns> private static float GetPenetrationValue(DamageInfo dinfo) { if (dinfo.Def.isExplosive) { return(dinfo.Amount * 0.1f); // Explosions have 10% of their damage as penetration } if (dinfo.Weapon != null) { // Case 1: projectile attack ProjectilePropertiesCE projectileProps = dinfo.Weapon.projectile as ProjectilePropertiesCE; if (projectileProps != null) { return(projectileProps.armorPenetration); } // Case 2: melee attack Pawn instigatorPawn = dinfo.Instigator as Pawn; if (instigatorPawn != null) { // Case 2.1: .. of an equiped melee weapon if (dinfo.Weapon.IsMeleeWeapon) { ThingWithComps equipment = instigatorPawn.equipment?.Primary; if (equipment == null || equipment.def != dinfo.Weapon) { Log.Error("CE tried getting armor penetration from melee weapon " + dinfo.Weapon.defName + " but instigator " + dinfo.Instigator.ToString() + " equipment does not match"); return(0); } var penetrationMult = equipment.GetStatValue(CE_StatDefOf.MeleePenetrationFactor); var tool = equipment.def.tools.OfType <ToolCE>().GetUsedTool(dinfo); return(tool.armorPenetration * penetrationMult); } // Case 2.2: .. of a ranged weapon if (dinfo.Weapon.IsRangedWeapon) { var tool = dinfo.Weapon.tools.OfType <ToolCE>().GetUsedTool(dinfo); return(tool.armorPenetration); } // Case 2.3: .. of the pawn if (instigatorPawn.def == dinfo.Weapon) { Verb availableVerb = instigatorPawn.meleeVerbs.TryGetMeleeVerb(); // Case 2.3.1: .. of a weaponized hediff (power claw, scyther blade) HediffCompProperties_VerbGiver compProps = dinfo.WeaponLinkedHediff?.CompPropsFor(typeof(HediffComp_VerbGiver)) as HediffCompProperties_VerbGiver; if (compProps != null) { var tool = compProps.tools.OfType <ToolCE>().GetUsedTool(dinfo); if (tool != null) { return(tool.armorPenetration); } VerbPropertiesCE verbProps = compProps.verbs?.FirstOrDefault(v => v is VerbPropertiesCE) as VerbPropertiesCE; var verbs = compProps.verbs; if (verbs.Count() > 1) { Log.ErrorOnce("CE :: HediffCompProperties_VerbGiver for " + dinfo.WeaponLinkedHediff + " has multiple VerbPropertiesCE. [While evaluating DamageInfo " + dinfo.ToString() + "]", dinfo.WeaponLinkedHediff.GetHashCode() + 128937921); } if (verbProps != null) { Log.ErrorOnce("CE :: HediffCompProperties_VerbGiver from DamageInfo " + dinfo.ToString() + " has VerbPropertiesCE, but these should be moved to <tools> for B18", dinfo.WeaponLinkedHediff.GetHashCode() + 128937921); return(verbProps.meleeArmorPenetration); } } // AllVerbs: bodyparts of the pawn // meleeVerbs: all verbs considered "melee worthy" // Case 2.4: .. of a tool naturally on the body (hands/fist, head) if (instigatorPawn.verbTracker != null && !instigatorPawn.verbTracker.AllVerbs.NullOrEmpty()) { var verbs = instigatorPawn.verbTracker.AllVerbs.Where(v => v.tool is ToolCE && v.tool.linkedBodyPartsGroup == dinfo.WeaponBodyPartGroup); if (verbs.Count() > 1) { Log.ErrorOnce("CE :: Race " + instigatorPawn.def + " has multiple ToolCE with linkedBodyPartsGroup=" + dinfo.WeaponBodyPartGroup.ToString() + " which can not be distunguished between. Consider using different linkedBodyPartsGroups. [While evaluating DamageInfo " + dinfo.ToString() + "]", instigatorPawn.def.GetHashCode() + 128937921); } if (!verbs.Any()) { Log.ErrorOnce("CE :: Pawn " + instigatorPawn.ToString() + " for BodyPartGroup " + dinfo.WeaponBodyPartGroup.ToString() + " could not find matching verb (in AllVerbs: " + String.Join(",", instigatorPawn.verbTracker.AllVerbs.Select(x => x.ToString()).ToArray()) + ") [While evaluating DamageInfo " + dinfo.ToString() + "]", instigatorPawn.def.GetHashCode() + 128937921); return(0); } return((verbs.First().tool as ToolCE).armorPenetration); } } } } #if DEBUG Log.Warning("CE could not determine armor penetration, defaulting"); #endif return(9999); // Really high default value so vanilla damage sources such as GiveInjuriesToKill always penetrate }