Sharp to blunt bugfix

Started by Currently_Fortifying, March 25, 2020, 06:46:06 PM

Previous topic - Next topic

Currently_Fortifying

Currently whenever sharp is converted to blunt, via ArmorUtility.ApplyArmor, DamageWorker_Blunt.ApplySpecialEffects is not called.
The reason of course is that the calculations for sharp damage, and its conversion to blunt, take place in the base class DamageWorker_AddInjury, which means the derived class, DamageWorker_Blunt, is never created and as such DamageWorker_AddInjury.ApplySpecialEffects is called.

The intended goal is to make a patch that checks to see if the damage type has been converted to blunt, and then uses DamageWorker_Blunt.ApplySpecialEffects.
The reason I want to use Blunt's special Effects is that I want other peoples' changes to Blunt's special effects to still work. Likewise I wanted to make a mod later down the line that overhauls how blunt damage effects internal parts.

I've been knocking my head against this problem in my free time for a few days now, and haven't made any progress.
As far as I can tell, tanspilers aren't an option as I need to call Blunt's code.
I would post my code, but I would prefer to hear what you guys think and start fresh.

Any help is appreciated.
I'm going to be away for several hours so I probably won't respond for a while.

LWM

Where does the conversion from sharp to blunt damage happen?

Currently_Fortifying

In ArmorUtility.ApplyArmor, it occurs at the very end of the function.

ArmorUtility.GetPostArmorDamage is called in DamageWorker_AddInjury.ApplyDamageToPart.
.ApplyArmor is called by .GetPostArmorDamage.

Do you want me to post the relevant parts from the decompiled source code?

Currently_Fortifying

Actually, I'll just go ahead and post the source.
DamageWorker_AddInjury
(namespace Verse
{
// Token: 0x02000222 RID: 546
public class DamageWorker_AddInjury : DamageWorker
{
// Token: 0x06000F27 RID: 3879 RVA: 0x00056A98 File Offset: 0x00054C98
public override DamageWorker.DamageResult Apply(DamageInfo dinfo, Thing thing)
{
Pawn pawn = thing as Pawn;
if (pawn == null)
{
return base.Apply(dinfo, thing);
}
return this.ApplyToPawn(dinfo, pawn);
}

// Token: 0x06000F28 RID: 3880 RVA: 0x00056AC0 File Offset: 0x00054CC0
private DamageWorker.DamageResult ApplyToPawn(DamageInfo dinfo, Pawn pawn)
{
DamageWorker.DamageResult damageResult = new DamageWorker.DamageResult();
if (dinfo.Amount <= 0f)
{
return damageResult;
}
if (!DebugSettings.enablePlayerDamage && pawn.Faction == Faction.OfPlayer)
{
return damageResult;
}
Map mapHeld = pawn.MapHeld;
bool spawnedOrAnyParentSpawned = pawn.SpawnedOrAnyParentSpawned;
if (dinfo.AllowDamagePropagation && dinfo.Amount >= (float)dinfo.Def.minDamageToFragment)
{
int num = Rand.RangeInclusive(2, 4);
for (int i = 0; i < num; i++)
{
DamageInfo dinfo2 = dinfo;
dinfo2.SetAmount(dinfo.Amount / (float)num);
this.ApplyDamageToPart(dinfo2, pawn, damageResult);
}
}
else
{
this.ApplyDamageToPart(dinfo, pawn, damageResult);
this.ApplySmallPawnDamagePropagation(dinfo, pawn, damageResult);
}
if (damageResult.wounded)
{
DamageWorker_AddInjury.PlayWoundedVoiceSound(dinfo, pawn);
pawn.Drawer.Notify_DamageApplied(dinfo);
EffecterDef damageEffecter = pawn.RaceProps.FleshType.damageEffecter;
if (damageEffecter != null)
{
if (pawn.health.woundedEffecter != null && pawn.health.woundedEffecter.def != damageEffecter)
{
pawn.health.woundedEffecter.Cleanup();
}
pawn.health.woundedEffecter = damageEffecter.Spawn();
pawn.health.woundedEffecter.Trigger(pawn, dinfo.Instigator ?? pawn);
}
}
if (damageResult.headshot && pawn.Spawned)
{
MoteMaker.ThrowText(new Vector3((float)pawn.Position.x + 1f, (float)pawn.Position.y, (float)pawn.Position.z + 1f), pawn.Map, "Headshot".Translate(), Color.white, -1f);
if (dinfo.Instigator != null)
{
Pawn pawn2 = dinfo.Instigator as Pawn;
if (pawn2 != null)
{
pawn2.records.Increment(RecordDefOf.Headshots);
}
}
}
if ((damageResult.deflected || damageResult.diminished) && spawnedOrAnyParentSpawned)
{
EffecterDef effecterDef;
if (damageResult.deflected)
{
if (damageResult.deflectedByMetalArmor && dinfo.Def.canUseDeflectMetalEffect)
{
if (dinfo.Def == DamageDefOf.Bullet)
{
effecterDef = EffecterDefOf.Deflect_Metal_Bullet;
}
else
{
effecterDef = EffecterDefOf.Deflect_Metal;
}
}
else if (dinfo.Def == DamageDefOf.Bullet)
{
effecterDef = EffecterDefOf.Deflect_General_Bullet;
}
else
{
effecterDef = EffecterDefOf.Deflect_General;
}
}
else if (damageResult.diminishedByMetalArmor)
{
effecterDef = EffecterDefOf.DamageDiminished_Metal;
}
else
{
effecterDef = EffecterDefOf.DamageDiminished_General;
}
if (pawn.health.deflectionEffecter == null || pawn.health.deflectionEffecter.def != effecterDef)
{
if (pawn.health.deflectionEffecter != null)
{
pawn.health.deflectionEffecter.Cleanup();
pawn.health.deflectionEffecter = null;
}
pawn.health.deflectionEffecter = effecterDef.Spawn();
}
pawn.health.deflectionEffecter.Trigger(pawn, dinfo.Instigator ?? pawn);
if (damageResult.deflected)
{
pawn.Drawer.Notify_DamageDeflected(dinfo);
}
}
if (!damageResult.deflected && spawnedOrAnyParentSpawned)
{
ImpactSoundUtility.PlayImpactSound(pawn, dinfo.Def.impactSoundType, mapHeld);
}
return damageResult;
}

// Token: 0x06000F29 RID: 3881 RVA: 0x00056DF0 File Offset: 0x00054FF0
private void CheckApplySpreadDamage(DamageInfo dinfo, Thing t)
{
if (dinfo.Def == DamageDefOf.Flame && !t.FlammableNow)
{
return;
}
if (Rand.Chance(0.5f))
{
dinfo.SetAmount((float)Mathf.CeilToInt(dinfo.Amount * Rand.Range(0.35f, 0.7f)));
t.TakeDamage(dinfo);
}
}

// Token: 0x06000F2A RID: 3882 RVA: 0x00056E4C File Offset: 0x0005504C
private void ApplySmallPawnDamagePropagation(DamageInfo dinfo, Pawn pawn, DamageWorker.DamageResult result)
{
if (!dinfo.AllowDamagePropagation)
{
return;
}
if (result.LastHitPart != null && dinfo.Def.harmsHealth && result.LastHitPart != pawn.RaceProps.body.corePart && result.LastHitPart.parent != null && pawn.health.hediffSet.GetPartHealth(result.LastHitPart.parent) > 0f && result.LastHitPart.parent.coverageAbs > 0f && dinfo.Amount >= 10f && pawn.HealthScale <= 0.5001f)
{
DamageInfo dinfo2 = dinfo;
dinfo2.SetHitPart(result.LastHitPart.parent);
this.ApplyDamageToPart(dinfo2, pawn, result);
}
}

// Token: 0x06000F2B RID: 3883 RVA: 0x00056F18 File Offset: 0x00055118
private void ApplyDamageToPart(DamageInfo dinfo, Pawn pawn, DamageWorker.DamageResult result)
{
BodyPartRecord exactPartFromDamageInfo = this.GetExactPartFromDamageInfo(dinfo, pawn);
if (exactPartFromDamageInfo == null)
{
return;
}
dinfo.SetHitPart(exactPartFromDamageInfo);
float num = dinfo.Amount;
bool flag = !dinfo.InstantPermanentInjury;
bool deflectedByMetalArmor = false;
if (flag)
{
DamageDef def = dinfo.Def;
bool diminishedByMetalArmor;
num = ArmorUtility.GetPostArmorDamage(pawn, num, dinfo.ArmorPenetrationInt, dinfo.HitPart, ref def, out deflectedByMetalArmor, out diminishedByMetalArmor);
dinfo.Def = def;
if (num < dinfo.Amount)
{
result.diminished = true;
result.diminishedByMetalArmor = diminishedByMetalArmor;
}
}
if (num <= 0f)
{
result.AddPart(pawn, dinfo.HitPart);
result.deflected = true;
result.deflectedByMetalArmor = deflectedByMetalArmor;
return;
}
if (DamageWorker_AddInjury.IsHeadshot(dinfo, pawn))
{
result.headshot = true;
}
if (dinfo.InstantPermanentInjury && (HealthUtility.GetHediffDefFromDamage(dinfo.Def, pawn, dinfo.HitPart).CompPropsFor(typeof(HediffComp_GetsPermanent)) == null || dinfo.HitPart.def.permanentInjuryChanceFactor == 0f || pawn.health.hediffSet.PartOrAnyAncestorHasDirectlyAddedParts(dinfo.HitPart)))
{
return;
}
if (!dinfo.AllowDamagePropagation)
{
this.FinalizeAndAddInjury(pawn, num, dinfo, result);
return;
}
this.ApplySpecialEffectsToPart(pawn, num, dinfo, result);
}

// Token: 0x06000F2C RID: 3884 RVA: 0x0005704A File Offset: 0x0005524A
protected virtual void ApplySpecialEffectsToPart(Pawn pawn, float totalDamage, DamageInfo dinfo, DamageWorker.DamageResult result)
{
totalDamage = this.ReduceDamageToPreserveOutsideParts(totalDamage, dinfo, pawn);
this.FinalizeAndAddInjury(pawn, totalDamage, dinfo, result);
this.CheckDuplicateDamageToOuterParts(dinfo, pawn, totalDamage, result);
}

// Token: 0x06000F2D RID: 3885 RVA: 0x00057070 File Offset: 0x00055270
protected float FinalizeAndAddInjury(Pawn pawn, float totalDamage, DamageInfo dinfo, DamageWorker.DamageResult result)
{
if (pawn.health.hediffSet.PartIsMissing(dinfo.HitPart))
{
return 0f;
}
if (dinfo.Def.ExternalViolenceFor(pawn))
{
totalDamage *= pawn.GetStatValue(StatDefOf.IncomingDamageFactor, true);
}
HediffDef hediffDefFromDamage = HealthUtility.GetHediffDefFromDamage(dinfo.Def, pawn, dinfo.HitPart);
Hediff_Injury hediff_Injury = (Hediff_Injury)HediffMaker.MakeHediff(hediffDefFromDamage, pawn, null);
hediff_Injury.Part = dinfo.HitPart;
hediff_Injury.source = dinfo.Weapon;
hediff_Injury.sourceBodyPartGroup = dinfo.WeaponBodyPartGroup;
hediff_Injury.sourceHediffDef = dinfo.WeaponLinkedHediff;
hediff_Injury.Severity = totalDamage;
if (dinfo.InstantPermanentInjury)
{
HediffComp_GetsPermanent hediffComp_GetsPermanent = hediff_Injury.TryGetComp<HediffComp_GetsPermanent>();
if (hediffComp_GetsPermanent != null)
{
hediffComp_GetsPermanent.IsPermanent = true;
}
else
{
Log.Error(string.Concat(new object[]
{
"Tried to create instant permanent injury on Hediff without a GetsPermanent comp: ",
hediffDefFromDamage,
" on ",
pawn
}), false);
}
}
return this.FinalizeAndAddInjury(pawn, hediff_Injury, dinfo, result);
}

// Token: 0x06000F2E RID: 3886 RVA: 0x0005716C File Offset: 0x0005536C
protected float FinalizeAndAddInjury(Pawn pawn, Hediff_Injury injury, DamageInfo dinfo, DamageWorker.DamageResult result)
{
HediffComp_GetsPermanent hediffComp_GetsPermanent = injury.TryGetComp<HediffComp_GetsPermanent>();
if (hediffComp_GetsPermanent != null)
{
hediffComp_GetsPermanent.PreFinalizeInjury();
}
pawn.health.AddHediff(injury, null, new DamageInfo?(dinfo), result);
float num = Mathf.Min(injury.Severity, pawn.health.hediffSet.GetPartHealth(injury.Part));
result.totalDamageDealt += num;
result.wounded = true;
result.AddPart(pawn, injury.Part);
result.AddHediff(injury);
return num;
}

// Token: 0x06000F2F RID: 3887 RVA: 0x000571F0 File Offset: 0x000553F0
private void CheckDuplicateDamageToOuterParts(DamageInfo dinfo, Pawn pawn, float totalDamage, DamageWorker.DamageResult result)
{
if (!dinfo.AllowDamagePropagation)
{
return;
}
if (dinfo.Def.harmAllLayersUntilOutside && dinfo.HitPart.depth == BodyPartDepth.Inside)
{
BodyPartRecord parent = dinfo.HitPart.parent;
do
{
if (pawn.health.hediffSet.GetPartHealth(parent) != 0f && parent.coverageAbs > 0f)
{
Hediff_Injury hediff_Injury = (Hediff_Injury)HediffMaker.MakeHediff(HealthUtility.GetHediffDefFromDamage(dinfo.Def, pawn, parent), pawn, null);
hediff_Injury.Part = parent;
hediff_Injury.source = dinfo.Weapon;
hediff_Injury.sourceBodyPartGroup = dinfo.WeaponBodyPartGroup;
hediff_Injury.Severity = totalDamage;
if (hediff_Injury.Severity <= 0f)
{
hediff_Injury.Severity = 1f;
}
this.FinalizeAndAddInjury(pawn, hediff_Injury, dinfo, result);
}
if (parent.depth == BodyPartDepth.Outside)
{
break;
}
parent = parent.parent;
}
while (parent != null);
}
}

// Token: 0x06000F30 RID: 3888 RVA: 0x000572D9 File Offset: 0x000554D9
private static bool IsHeadshot(DamageInfo dinfo, Pawn pawn)
{
return !dinfo.InstantPermanentInjury && dinfo.HitPart.groups.Contains(BodyPartGroupDefOf.FullHead) && dinfo.Def == DamageDefOf.Bullet;
}

// Token: 0x06000F31 RID: 3889 RVA: 0x00057310 File Offset: 0x00055510
private BodyPartRecord GetExactPartFromDamageInfo(DamageInfo dinfo, Pawn pawn)
{
if (dinfo.HitPart == null)
{
BodyPartRecord bodyPartRecord = this.ChooseHitPart(dinfo, pawn);
if (bodyPartRecord == null)
{
Log.Warning("ChooseHitPart returned null (any part).", false);
}
return bodyPartRecord;
}
if (!pawn.health.hediffSet.GetNotMissingParts(BodyPartHeight.Undefined, BodyPartDepth.Undefined, null, null).Any((BodyPartRecord x) => x == dinfo.HitPart))
{
return null;
}
return dinfo.HitPart;
}

// Token: 0x06000F32 RID: 3890 RVA: 0x00057387 File Offset: 0x00055587
protected virtual BodyPartRecord ChooseHitPart(DamageInfo dinfo, Pawn pawn)
{
return pawn.health.hediffSet.GetRandomNotMissingPart(dinfo.Def, dinfo.Height, dinfo.Depth, null);
}

// Token: 0x06000F33 RID: 3891 RVA: 0x000573B0 File Offset: 0x000555B0
private static void PlayWoundedVoiceSound(DamageInfo dinfo, Pawn pawn)
{
if (pawn.Dead)
{
return;
}
if (dinfo.InstantPermanentInjury)
{
return;
}
if (!pawn.SpawnedOrAnyParentSpawned)
{
return;
}
if (dinfo.Def.ExternalViolenceFor(pawn))
{
LifeStageUtility.PlayNearestLifestageSound(pawn, (LifeStageAge ls) => ls.soundWounded, 1f);
}
}

// Token: 0x06000F34 RID: 3892 RVA: 0x00057414 File Offset: 0x00055614
protected float ReduceDamageToPreserveOutsideParts(float postArmorDamage, DamageInfo dinfo, Pawn pawn)
{
if (!DamageWorker_AddInjury.ShouldReduceDamageToPreservePart(dinfo.HitPart))
{
return postArmorDamage;
}
float partHealth = pawn.health.hediffSet.GetPartHealth(dinfo.HitPart);
if (postArmorDamage < partHealth)
{
return postArmorDamage;
}
float maxHealth = dinfo.HitPart.def.GetMaxHealth(pawn);
float f = (postArmorDamage - partHealth) / maxHealth;
if (Rand.Chance(this.def.overkillPctToDestroyPart.InverseLerpThroughRange(f)))
{
return postArmorDamage;
}
return postArmorDamage = partHealth - 1f;
}

// Token: 0x06000F35 RID: 3893 RVA: 0x0005748C File Offset: 0x0005568C
public static bool ShouldReduceDamageToPreservePart(BodyPartRecord bodyPart)
{
return bodyPart.depth == BodyPartDepth.Outside && !bodyPart.IsCorePart;
}
}
}
)

Currently_Fortifying

DamageWorker_Blunt
(namespace Verse
{
// Token: 0x02000224 RID: 548
public class DamageWorker_Blunt : DamageWorker_AddInjury
{
// Token: 0x06000F39 RID: 3897 RVA: 0x000574AA File Offset: 0x000556AA
protected override BodyPartRecord ChooseHitPart(DamageInfo dinfo, Pawn pawn)
{
return pawn.health.hediffSet.GetRandomNotMissingPart(dinfo.Def, dinfo.Height, BodyPartDepth.Outside, null);
}

// Token: 0x06000F3A RID: 3898 RVA: 0x000574D4 File Offset: 0x000556D4
protected override void ApplySpecialEffectsToPart(Pawn pawn, float totalDamage, DamageInfo dinfo, DamageWorker.DamageResult result)
{
bool flag = Rand.Chance(this.def.bluntInnerHitChance);
float num = flag ? this.def.bluntInnerHitDamageFractionToConvert.RandomInRange : 0f;
float num2 = totalDamage * (1f - num);
DamageInfo lastInfo = dinfo;
for (;;)
{
num2 -= base.FinalizeAndAddInjury(pawn, num2, lastInfo, result);
if (!pawn.health.hediffSet.PartIsMissing(lastInfo.HitPart) || num2 <= 1f)
{
break;
}
BodyPartRecord parent = lastInfo.HitPart.parent;
if (parent == null)
{
break;
}
lastInfo.SetHitPart(parent);
}
if (flag && !lastInfo.HitPart.def.IsSolid(lastInfo.HitPart, pawn.health.hediffSet.hediffs) && lastInfo.HitPart.depth == BodyPartDepth.Outside)
{
BodyPartRecord hitPart;
if ((from x in pawn.health.hediffSet.GetNotMissingParts(BodyPartHeight.Undefined, BodyPartDepth.Undefined, null, null)
where x.parent == lastInfo.HitPart && x.def.IsSolid(x, pawn.health.hediffSet.hediffs) && x.depth == BodyPartDepth.Inside
select x).TryRandomElementByWeight((BodyPartRecord x) => x.coverageAbs, out hitPart))
{
DamageInfo lastInfo2 = lastInfo;
lastInfo2.SetHitPart(hitPart);
float totalDamage2 = totalDamage * num + totalDamage * this.def.bluntInnerHitDamageFractionToAdd.RandomInRange;
base.FinalizeAndAddInjury(pawn, totalDamage2, lastInfo2, result);
}
}
if (!pawn.Dead)
{
SimpleCurve simpleCurve = null;
if (lastInfo.HitPart.parent == null)
{
simpleCurve = this.def.bluntStunChancePerDamagePctOfCorePartToBodyCurve;
}
else
{
foreach (BodyPartRecord lhs in pawn.RaceProps.body.GetPartsWithTag(BodyPartTagDefOf.ConsciousnessSource))
{
if (this.InSameBranch(lhs, lastInfo.HitPart))
{
simpleCurve = this.def.bluntStunChancePerDamagePctOfCorePartToHeadCurve;
break;
}
}
}
if (simpleCurve != null)
{
float x2 = totalDamage / pawn.def.race.body.corePart.def.GetMaxHealth(pawn);
if (Rand.Chance(simpleCurve.Evaluate(x2)))
{
DamageInfo dinfo2 = dinfo;
dinfo2.Def = DamageDefOf.Stun;
dinfo2.SetAmount((float)this.def.bluntStunDuration.SecondsToTicks() / 30f);
pawn.TakeDamage(dinfo2);
}
}
}
}

// Token: 0x06000F3B RID: 3899 RVA: 0x000577A0 File Offset: 0x000559A0
[DebugOutput]
public static void StunChances()
{
Func<ThingDef, float, bool, string> bluntBodyStunChance = delegate(ThingDef d, float dam, bool onHead)
{
SimpleCurve simpleCurve = onHead ? DamageDefOf.Blunt.bluntStunChancePerDamagePctOfCorePartToHeadCurve : DamageDefOf.Blunt.bluntStunChancePerDamagePctOfCorePartToBodyCurve;
Pawn pawn = PawnGenerator.GeneratePawn(new PawnGenerationRequest(d.race.AnyPawnKind, Find.FactionManager.FirstFactionOfDef(d.race.AnyPawnKind.defaultFactionType), PawnGenerationContext.NonPlayer, -1, true, false, false, false, true, false, 1f, false, true, true, true, false, false, false, false, 0f, null, 1f, null, null, null, null, null, null, null, null, null, null, null, null));
float x = dam / d.race.body.corePart.def.GetMaxHealth(pawn);
Find.WorldPawns.PassToWorld(pawn, PawnDiscardDecideMode.Discard);
return Mathf.Clamp01(simpleCurve.Evaluate(x)).ToStringPercent();
};
List<TableDataGetter<ThingDef>> list = new List<TableDataGetter<ThingDef>>();
list.Add(new TableDataGetter<ThingDef>("defName", (ThingDef d) => d.defName));
list.Add(new TableDataGetter<ThingDef>("body size", (ThingDef d) => d.race.baseBodySize.ToString("F2")));
list.Add(new TableDataGetter<ThingDef>("health scale", (ThingDef d) => d.race.baseHealthScale.ToString("F2")));
list.Add(new TableDataGetter<ThingDef>("body size\n* health scale", (ThingDef d) => (d.race.baseHealthScale * d.race.baseBodySize).ToString("F2")));
list.Add(new TableDataGetter<ThingDef>("core part\nhealth", delegate(ThingDef d)
{
Pawn pawn = PawnGenerator.GeneratePawn(new PawnGenerationRequest(d.race.AnyPawnKind, Find.FactionManager.FirstFactionOfDef(d.race.AnyPawnKind.defaultFactionType), PawnGenerationContext.NonPlayer, -1, true, false, false, false, true, false, 1f, false, true, true, true, false, false, false, false, 0f, null, 1f, null, null, null, null, null, null, null, null, null, null, null, null));
float maxHealth = d.race.body.corePart.def.GetMaxHealth(pawn);
Find.WorldPawns.PassToWorld(pawn, PawnDiscardDecideMode.Discard);
return maxHealth;
}));
list.Add(new TableDataGetter<ThingDef>("stun\nchance\nbody\n5", (ThingDef d) => bluntBodyStunChance(d, 5f, false)));
list.Add(new TableDataGetter<ThingDef>("stun\nchance\nbody\n10", (ThingDef d) => bluntBodyStunChance(d, 10f, false)));
list.Add(new TableDataGetter<ThingDef>("stun\nchance\nbody\n15", (ThingDef d) => bluntBodyStunChance(d, 15f, false)));
list.Add(new TableDataGetter<ThingDef>("stun\nchance\nbody\n20", (ThingDef d) => bluntBodyStunChance(d, 20f, false)));
list.Add(new TableDataGetter<ThingDef>("stun\nchance\nhead\n5", (ThingDef d) => bluntBodyStunChance(d, 5f, true)));
list.Add(new TableDataGetter<ThingDef>("stun\nchance\nhead\n10", (ThingDef d) => bluntBodyStunChance(d, 10f, true)));
list.Add(new TableDataGetter<ThingDef>("stun\nchance\nhead\n15", (ThingDef d) => bluntBodyStunChance(d, 15f, true)));
list.Add(new TableDataGetter<ThingDef>("stun\nchance\nhead\n20", (ThingDef d) => bluntBodyStunChance(d, 20f, true)));
DebugTables.MakeTablesDialog<ThingDef>(from d in DefDatabase<ThingDef>.AllDefs
where d.category == ThingCategory.Pawn
select d, list.ToArray());
}

// Token: 0x06000F3C RID: 3900 RVA: 0x000579E0 File Offset: 0x00055BE0
private bool InSameBranch(BodyPartRecord lhs, BodyPartRecord rhs)
{
while (lhs.parent != null)
{
if (lhs.parent.parent == null)
{
break;
}
lhs = lhs.parent;
}
while (rhs.parent != null && rhs.parent.parent != null)
{
rhs = rhs.parent;
}
return lhs == rhs;
}
}
}
)

Currently_Fortifying

ArmorUtility
(namespace Verse
{
// Token: 0x0200022B RID: 555
public static class ArmorUtility
{
// Token: 0x06000F4E RID: 3918 RVA: 0x000580B4 File Offset: 0x000562B4
public static float GetPostArmorDamage(Pawn pawn, float amount, float armorPenetration, BodyPartRecord part, ref DamageDef damageDef, out bool deflectedByMetalArmor, out bool diminishedByMetalArmor)
{
deflectedByMetalArmor = false;
diminishedByMetalArmor = false;
if (damageDef.armorCategory == null)
{
return amount;
}
StatDef armorRatingStat = damageDef.armorCategory.armorRatingStat;
if (pawn.apparel != null)
{
List<Apparel> wornApparel = pawn.apparel.WornApparel;
for (int i = wornApparel.Count - 1; i >= 0; i--)
{
Apparel apparel = wornApparel[i];
if (apparel.def.apparel.CoversBodyPart(part))
{
float num = amount;
bool flag;
ArmorUtility.ApplyArmor(ref amount, armorPenetration, apparel.GetStatValue(armorRatingStat, true), apparel, ref damageDef, pawn, out flag);
if (amount < 0.001f)
{
deflectedByMetalArmor = flag;
return 0f;
}
if (amount < num && flag)
{
diminishedByMetalArmor = true;
}
}
}
}
float num2 = amount;
bool flag2;
ArmorUtility.ApplyArmor(ref amount, armorPenetration, pawn.GetStatValue(armorRatingStat, true), null, ref damageDef, pawn, out flag2);
if (amount < 0.001f)
{
deflectedByMetalArmor = flag2;
return 0f;
}
if (amount < num2 && flag2)
{
diminishedByMetalArmor = true;
}
return amount;
}

// Token: 0x06000F4F RID: 3919 RVA: 0x000581A0 File Offset: 0x000563A0
private static void ApplyArmor(ref float damAmount, float armorPenetration, float armorRating, Thing armorThing, ref DamageDef damageDef, Pawn pawn, out bool metalArmor)
{
if (armorThing != null)
{
metalArmor = (armorThing.def.apparel.useDeflectMetalEffect || (armorThing.Stuff != null && armorThing.Stuff.IsMetal));
}
else
{
metalArmor = pawn.RaceProps.IsMechanoid;
}
if (armorThing != null)
{
float f = damAmount * 0.25f;
armorThing.TakeDamage(new DamageInfo(damageDef, (float)GenMath.RoundRandom(f), 0f, -1f, null, null, null, DamageInfo.SourceCategory.ThingOrUnknown, null));
}
float num = Mathf.Max(armorRating - armorPenetration, 0f);
float value = Rand.Value;
float num2 = num * 0.5f;
float num3 = num;
if (value < num2)
{
damAmount = 0f;
return;
}
if (value < num3)
{
damAmount = (float)GenMath.RoundRandom(damAmount / 2f);
if (damageDef.armorCategory == DamageArmorCategoryDefOf.Sharp)
{
damageDef = DamageDefOf.Blunt;
}
}
}

// Token: 0x04000B59 RID: 2905
public const float MaxArmorRating = 2f;

// Token: 0x04000B5A RID: 2906
public const float DeflectThresholdFactor = 0.5f;
}
}
)

LWM

Quote from: Currently_Fortifying on March 26, 2020, 03:40:54 AM
Do you want me to post the relevant parts from the decompiled source code?

Can you walk me through how damage happens?  So that pawn gets hit by a bullet, and then what?