From a94e91822bf5f91ec805cbedd89b5bf20cfbf746 Mon Sep 17 00:00:00 2001
From: ViralReaction <136116069+ViralReaction@users.noreply.github.com>
Date: Sat, 17 Aug 2024 12:22:13 -0600
Subject: [PATCH 1/4] Adds information for shields to UI
* Shows what the shield protects in the information tab
* Shows what the shield protects in the Gear ITab when hovered over
* Armor values in GearTab properly calculate shield armor
---
Languages/English/Keyed/Keys.xml | 4 +
.../CombatExtended/Defs/ShieldDefExtension.cs | 7 ++
.../CombatExtended/Loadouts/ITab_Inventory.cs | 10 +++
.../CombatExtended/Things/Apparel_Shield.cs | 26 +++++-
.../Harmony/Harmony_ThingDef.cs | 80 +++++++++++++++++++
5 files changed, 126 insertions(+), 1 deletion(-)
diff --git a/Languages/English/Keyed/Keys.xml b/Languages/English/Keyed/Keys.xml
index 471ece0cef..0ec88643b2 100644
--- a/Languages/English/Keyed/Keys.xml
+++ b/Languages/English/Keyed/Keys.xml
@@ -161,5 +161,9 @@
This weapon is yet patched for CE, while it will behave in vanilla way as fallback, please contact a patcher for compatibility patches.
Not patched for CE
+
+
+ Shield Protects
+ Shields protect parts of the body from ranged and melee attacks.
\ No newline at end of file
diff --git a/Source/CombatExtended/CombatExtended/Defs/ShieldDefExtension.cs b/Source/CombatExtended/CombatExtended/Defs/ShieldDefExtension.cs
index 0e22f414f8..5d9182e56c 100644
--- a/Source/CombatExtended/CombatExtended/Defs/ShieldDefExtension.cs
+++ b/Source/CombatExtended/CombatExtended/Defs/ShieldDefExtension.cs
@@ -38,5 +38,12 @@ public bool PartIsCoveredByShield(BodyPartRecord part, Pawn pawn)
}
return false;
}
+ public static string GetShieldProtectedAreas(BodyDef body, ThingDef thingDef)
+ {
+ return (from part in (from x in body.AllParts
+ where x.depth == BodyPartDepth.Outside && x.groups.Any((BodyPartGroupDef y) => thingDef.GetModExtension().shieldCoverage.Contains(y))
+ select x).Distinct()
+ select part.Label).ToCommaList(false, false).CapitalizeFirst();
+ }
}
}
diff --git a/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs b/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs
index 378fe1870a..8073e25321 100644
--- a/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs
+++ b/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs
@@ -8,6 +8,7 @@
using Verse;
using Verse.AI;
using Verse.Sound;
+using CombatExtended.HarmonyCE;
namespace CombatExtended
{
@@ -517,6 +518,7 @@ private void RebuildArmorCache(Dictionary armorCache, Sta
armorCache.Clear();
float naturalArmor = SelPawnForGear.GetStatValue(stat);
List wornApparel = SelPawnForGear.apparel?.WornApparel;
+ var shield = wornApparel.FirstOrDefault(x => x is Apparel_Shield);
foreach (BodyPartRecord part in SelPawnForGear.RaceProps.body.AllParts)
{
//TODO: 1.5 should be Neck
@@ -533,6 +535,14 @@ private void RebuildArmorCache(Dictionary armorCache, Sta
}
}
}
+ if (shield != null)
+ {
+ var shieldCoverage = shield.def.GetModExtension().PartIsCoveredByShield(part, SelPawnForGear);
+ if(shieldCoverage)
+ {
+ armorValue += shield.GetStatValue(stat);
+ }
+ }
armorCache[part] = armorValue;
}
}
diff --git a/Source/CombatExtended/CombatExtended/Things/Apparel_Shield.cs b/Source/CombatExtended/CombatExtended/Things/Apparel_Shield.cs
index 2aede24fc1..2eff7a479c 100644
--- a/Source/CombatExtended/CombatExtended/Things/Apparel_Shield.cs
+++ b/Source/CombatExtended/CombatExtended/Things/Apparel_Shield.cs
@@ -36,7 +36,30 @@ public override bool AllowVerbCast(Verb verb)
ThingWithComps primary = Wearer.equipment?.Primary;
return primary == null || (primary.def.weaponTags?.Contains(OneHandedTag) ?? false);
}
-
+ public override IEnumerable SpecialDisplayStats()
+ {
+ foreach (StatDrawEntry statDrawEntry in base.SpecialDisplayStats())
+ {
+ yield return statDrawEntry;
+ }
+ RoyalTitleDef royalTitleDef = (from t in DefDatabase.AllDefsListForReading.SelectMany((FactionDef f) => f.RoyalTitlesAwardableInSeniorityOrderForReading)
+ where t.requiredApparel != null && t.requiredApparel.Any((ApparelRequirement req) => req.ApparelMeetsRequirement(this.def, false))
+ orderby t.seniority descending
+ select t).FirstOrDefault();
+ if (royalTitleDef != null)
+ {
+ yield return new StatDrawEntry(StatCategoryDefOf.Apparel, "Stat_Thing_Apparel_MaxSatisfiedTitle".Translate(), royalTitleDef.GetLabelCapForBothGenders(), "Stat_Thing_Apparel_MaxSatisfiedTitle_Desc".Translate(), 2752, null, new Dialog_InfoCard.Hyperlink[]
+ {
+ new Dialog_InfoCard.Hyperlink(royalTitleDef, -1)
+ }, false, false);
+ }
+ var shieldCoverage = this.def.GetModExtension()?.shieldCoverage;
+ if (shieldCoverage != null)
+ {
+ yield return new StatDrawEntry(StatCategoryDefOf.Apparel, "CE_Shield_Coverage".Translate(), ShieldDefExtension.GetShieldProtectedAreas(BodyDefOf.Human, this.def), "CE_Shield_Coverage_Desc".Translate(), 800, null);
+ }
+ yield break;
+ }
public override void DrawWornExtras()
{
if (Wearer == null || !Wearer.Spawned)
@@ -94,6 +117,7 @@ public override void DrawWornExtras()
Matrix4x4 matrix = default(Matrix4x4);
matrix.SetTRS(vector, Quaternion.AngleAxis(num, Vector3.up), s);
Graphics.DrawMesh(MeshPool.plane10, matrix, mat, 0);
+
}
}
}
diff --git a/Source/CombatExtended/Harmony/Harmony_ThingDef.cs b/Source/CombatExtended/Harmony/Harmony_ThingDef.cs
index 3e17aa2e3a..4c6a5fe01b 100644
--- a/Source/CombatExtended/Harmony/Harmony_ThingDef.cs
+++ b/Source/CombatExtended/Harmony/Harmony_ThingDef.cs
@@ -2,7 +2,10 @@
using System.Globalization;
using System.Linq;
using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
using HarmonyLib;
+using Mono.Cecil.Cil;
using RimWorld;
using Verse;
using Verse.Noise;
@@ -140,4 +143,81 @@ public static void Postfix(ThingDef __instance)
}
}
}
+ [HarmonyPatch(typeof(ThingDef), "DescriptionDetailed", MethodType.Getter)]
+
+ internal static class ThingDef_DescriptionDetailed
+ {
+ private static StringBuilder AddShieldCover(ThingDef thingDef, StringBuilder stringBuilder)
+ {
+ if (thingDef.GetModExtension()?.shieldCoverage != null)
+ {
+ stringBuilder.Append(string.Format("{0}: {1}", "CE_Shield_Coverage".Translate(), ShieldDefExtension.GetShieldProtectedAreas(BodyDefOf.Human, thingDef)));
+ }
+ else
+ {
+ stringBuilder.Append(string.Format("{0}: {1}", "Covers".Translate(), thingDef.apparel.GetCoveredOuterPartsString(BodyDefOf.Human)));
+ }
+ return stringBuilder;
+ }
+ internal static IEnumerable Transpiler(IEnumerable instructions,
+ ILGenerator generator)
+ {
+ var code = new List(instructions);
+ int startIndex = -1;
+ int endIndex = -1;
+ bool foundCovers = false;
+
+ for (int i = 0; i < code.Count; i++)
+ {
+ if (code[i].opcode == OpCodes.Ldloc_0)
+ {
+ startIndex = i;
+
+ // Search for the next Pop, and check if "Covers" is in between
+ for (int j = i + 1; j < code.Count; j++)
+ {
+ if (code[j].opcode == OpCodes.Ldstr && code[j].operand as string == "Covers")
+ {
+ foundCovers = true;
+ }
+
+ if (code[j].opcode == OpCodes.Pop)
+ {
+ if (foundCovers)
+ {
+ endIndex = j;
+ break;
+ }
+ else
+ {
+ // If no "Covers" was found, reset startIndex and move to the next possible sequence
+ startIndex = -1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (endIndex > -1)
+ {
+ break;
+ }
+ }
+
+ // Remove the code between startIndex and endIndex if a valid range was found
+ if (startIndex > -1 && endIndex > -1)
+ {
+ code[startIndex].opcode = OpCodes.Nop;
+ //code[endIndex].opcode = OpCodes.Nop;
+ code.RemoveRange(startIndex + 1, endIndex-startIndex-1);
+ code.Insert(startIndex+1, new CodeInstruction(OpCodes.Ldarg_0));
+ code.Insert(startIndex+2, new CodeInstruction(OpCodes.Ldloc_0));
+ code.Insert(startIndex+3, new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(ThingDef_DescriptionDetailed), "AddShieldCover", null, null)));
+ }
+ foreach (var c in code)
+ {
+ yield return c;
+ }
+ }
+ }
}
From 2081ac07b671f8827ed29979ada30e94083ddc11 Mon Sep 17 00:00:00 2001
From: ViralReaction <136116069+ViralReaction@users.noreply.github.com>
Date: Tue, 20 Aug 2024 19:15:23 -0600
Subject: [PATCH 2/4] Housekeeping
---
.../CombatExtended/Loadouts/ITab_Inventory.cs | 4 ++--
.../CombatExtended/Things/Apparel_Shield.cs | 2 +-
Source/CombatExtended/Harmony/Harmony_ThingDef.cs | 8 ++++----
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs b/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs
index 8073e25321..265ce3350e 100644
--- a/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs
+++ b/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs
@@ -518,7 +518,7 @@ private void RebuildArmorCache(Dictionary armorCache, Sta
armorCache.Clear();
float naturalArmor = SelPawnForGear.GetStatValue(stat);
List wornApparel = SelPawnForGear.apparel?.WornApparel;
- var shield = wornApparel.FirstOrDefault(x => x is Apparel_Shield);
+ var shield = wornApparel.FirstOrDefault(x => x is Apparel_Shield);
foreach (BodyPartRecord part in SelPawnForGear.RaceProps.body.AllParts)
{
//TODO: 1.5 should be Neck
@@ -538,7 +538,7 @@ private void RebuildArmorCache(Dictionary armorCache, Sta
if (shield != null)
{
var shieldCoverage = shield.def.GetModExtension().PartIsCoveredByShield(part, SelPawnForGear);
- if(shieldCoverage)
+ if (shieldCoverage)
{
armorValue += shield.GetStatValue(stat);
}
diff --git a/Source/CombatExtended/CombatExtended/Things/Apparel_Shield.cs b/Source/CombatExtended/CombatExtended/Things/Apparel_Shield.cs
index 2eff7a479c..a31200c1a3 100644
--- a/Source/CombatExtended/CombatExtended/Things/Apparel_Shield.cs
+++ b/Source/CombatExtended/CombatExtended/Things/Apparel_Shield.cs
@@ -57,7 +57,7 @@ orderby t.seniority descending
if (shieldCoverage != null)
{
yield return new StatDrawEntry(StatCategoryDefOf.Apparel, "CE_Shield_Coverage".Translate(), ShieldDefExtension.GetShieldProtectedAreas(BodyDefOf.Human, this.def), "CE_Shield_Coverage_Desc".Translate(), 800, null);
- }
+ }
yield break;
}
public override void DrawWornExtras()
diff --git a/Source/CombatExtended/Harmony/Harmony_ThingDef.cs b/Source/CombatExtended/Harmony/Harmony_ThingDef.cs
index 4c6a5fe01b..9d0422bbe3 100644
--- a/Source/CombatExtended/Harmony/Harmony_ThingDef.cs
+++ b/Source/CombatExtended/Harmony/Harmony_ThingDef.cs
@@ -209,10 +209,10 @@ internal static IEnumerable Transpiler(IEnumerable
Date: Fri, 6 Sep 2024 01:11:52 -0400
Subject: [PATCH 3/4] Reivse keys
---
Languages/English/Keyed/Keys.xml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/Languages/English/Keyed/Keys.xml b/Languages/English/Keyed/Keys.xml
index 0ec88643b2..b5ed4a464e 100644
--- a/Languages/English/Keyed/Keys.xml
+++ b/Languages/English/Keyed/Keys.xml
@@ -122,10 +122,10 @@
°
m
Weapon penetration factor
- Skill factor
- Other factors
+ Skill factor
+ Other factors
Adjusted for weapon
- Penetration quality factor
+ Penetration quality factor
Damage per second
Damage variation
Final average damage
@@ -163,7 +163,7 @@
Not patched for CE
- Shield Protects
- Shields protect parts of the body from ranged and melee attacks.
+ Shield coverage
+ Body parts protected from ranged and melee attacks by the shield.
\ No newline at end of file
From 4d13ca0854b6918ea6e2c19604f3c9bdad1479c4 Mon Sep 17 00:00:00 2001
From: ViralReaction <136116069+ViralReaction@users.noreply.github.com>
Date: Sun, 8 Sep 2024 15:30:07 -0600
Subject: [PATCH 4/4] Add null check
* Adds null checks for the ITab Inventory when it comes to shield mod extension
---
.../CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs b/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs
index 265ce3350e..92d5c43ec3 100644
--- a/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs
+++ b/Source/CombatExtended/CombatExtended/Loadouts/ITab_Inventory.cs
@@ -537,8 +537,8 @@ private void RebuildArmorCache(Dictionary armorCache, Sta
}
if (shield != null)
{
- var shieldCoverage = shield.def.GetModExtension().PartIsCoveredByShield(part, SelPawnForGear);
- if (shieldCoverage)
+ var shieldCoverage = shield.def?.GetModExtension()?.PartIsCoveredByShield(part, SelPawnForGear);
+ if (shieldCoverage == true)
{
armorValue += shield.GetStatValue(stat);
}