Skip to content

Commit 7faabda

Browse files
Sync VEF graphic customization and Vanilla Persona Weapons Expanded (#466)
Updated graphic customization component from Vanilla Expanded Framework. This includes sync worker for the dialog and a couple of types used by it, syncing confirmation button, and closing the dialog/stopping synced methods if the dialog data is no longer valid. Vanilla Persona Weapons Expanded was also synced due to being based on graphic customization from VEF. It wasn't as straightforward as graphic customization was. The sync worker for the dialog reuses part of the code from VEF graphic customization one. It also required quite a bit more patches, for example to ensure the letter is not removed too early in MP, as well as some bug fixes for the mod itself (when selecting persona weapons without customization).
1 parent 537c31a commit 7faabda

File tree

2 files changed

+398
-0
lines changed

2 files changed

+398
-0
lines changed

Source/Mods/VanillaExpandedFramework.cs

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Linq;
55
using System.Reflection;
66
using System.Reflection.Emit;
7+
using System.Runtime.Serialization;
78
using HarmonyLib;
89
using Multiplayer.API;
910
using RimWorld;
@@ -48,6 +49,7 @@ public VanillaExpandedFramework(ModContentPack mod)
4849
(PatchWorkGiverDeliverResources, "Building stuff requiring non-construction skill", false),
4950
(PatchExpandableProjectile, "Expandable projectile", false),
5051
(PatchStaticCaches, "Static caches", false),
52+
(PatchGraphicCustomizationDialog, "Graphic Customization Dialog", true),
5153
];
5254

5355
foreach (var (patchMethod, componentName, latePatch) in patches)
@@ -1521,5 +1523,180 @@ private static bool NeverTimeoutDueToRealTime(ref bool __result, ref int ___Upda
15211523
}
15221524

15231525
#endregion
1526+
1527+
#region Graphic Customization Dialog
1528+
1529+
// Dialog_GraphicCustomization
1530+
private static Type graphicCustomizationDialogType;
1531+
internal static AccessTools.FieldRef<Window, ThingComp> graphicCustomizationCompField;
1532+
private static AccessTools.FieldRef<Window, ThingComp> graphicCustomizationGeneratedNamesCompField;
1533+
private static AccessTools.FieldRef<Window, Pawn> graphicCustomizationPawnField;
1534+
private static AccessTools.FieldRef<Window, IList> graphicCustomizationCurrentVariantsField;
1535+
private static AccessTools.FieldRef<Window, string> graphicCustomizationCurrentNameField;
1536+
1537+
// TextureVariant
1538+
private static SyncType variantsListType;
1539+
private static AccessTools.FieldRef<object, string> textureVariantTexNameField;
1540+
private static AccessTools.FieldRef<object, string> textureVariantTextureField;
1541+
private static AccessTools.FieldRef<object, string> textureVariantOutlineField;
1542+
private static AccessTools.FieldRef<object, object> textureVariantTextureVariantOverrideField;
1543+
private static AccessTools.FieldRef<object, float> textureVariantChanceOverrideField;
1544+
1545+
// TextureVariantOverride
1546+
private static SyncType textureVariantOverrideType;
1547+
private static AccessTools.FieldRef<object, float> textureVariantOverrideChanceField;
1548+
private static AccessTools.FieldRef<object, string> textureVariantOverrideGroupNameField;
1549+
private static AccessTools.FieldRef<object, string> textureVariantOverrideTexNameField;
1550+
1551+
private static void PatchGraphicCustomizationDialog()
1552+
{
1553+
var type = graphicCustomizationDialogType = AccessTools.TypeByName("GraphicCustomization.Dialog_GraphicCustomization");
1554+
graphicCustomizationCompField = AccessTools.FieldRefAccess<ThingComp>(type, "comp");
1555+
graphicCustomizationGeneratedNamesCompField = AccessTools.FieldRefAccess<ThingComp>(type, "compGeneratedName");
1556+
graphicCustomizationPawnField = AccessTools.FieldRefAccess<Pawn>(type, "pawn");
1557+
graphicCustomizationCurrentVariantsField = AccessTools.FieldRefAccess<IList>(type, "currentVariants");
1558+
graphicCustomizationCurrentNameField = AccessTools.FieldRefAccess<string>(type, "currentName");
1559+
// Accept customization
1560+
var method = MpMethodUtil.GetLambda(type, nameof(Window.DoWindowContents), 0);
1561+
MP.RegisterSyncMethod(method);
1562+
MpCompat.RegisterLambdaMethod(type, nameof(Window.DoWindowContents), 0);
1563+
MP.RegisterSyncWorker<Window>(SyncGraphicCustomizationDialog, type, true);
1564+
MpCompat.harmony.Patch(AccessTools.DeclaredMethod(type, "DrawConfirmButton"),
1565+
prefix: new HarmonyMethod(CloseGraphicCustomizationDialogOnAccept));
1566+
MpCompat.harmony.Patch(AccessTools.DeclaredMethod(type, nameof(Window.DoWindowContents)),
1567+
prefix: new HarmonyMethod(CloseGraphicCustomizationWhenNoLongerValid));
1568+
1569+
type = AccessTools.TypeByName("GraphicCustomization.TextureVariant");
1570+
variantsListType = typeof(List<>).MakeGenericType(type);
1571+
textureVariantTexNameField = AccessTools.FieldRefAccess<string>(type, "texName");
1572+
textureVariantTextureField = AccessTools.FieldRefAccess<string>(type, "texture");
1573+
textureVariantOutlineField = AccessTools.FieldRefAccess<string>(type, "outline");
1574+
textureVariantChanceOverrideField = AccessTools.FieldRefAccess<float>(type, "chanceOverride");
1575+
textureVariantTextureVariantOverrideField = AccessTools.FieldRefAccess<object>(type, "textureVariantOverride");
1576+
MP.RegisterSyncWorker<object>(SyncTextureVariant, type, shouldConstruct: true);
1577+
1578+
textureVariantOverrideType = type = AccessTools.TypeByName("GraphicCustomization.TextureVariantOverride");
1579+
textureVariantOverrideChanceField = AccessTools.FieldRefAccess<float>(type, "chance");
1580+
textureVariantOverrideGroupNameField = AccessTools.FieldRefAccess<string>(type, "groupName");
1581+
textureVariantOverrideTexNameField = AccessTools.FieldRefAccess<string>(type, "texName");
1582+
MP.RegisterSyncWorker<object>(SyncTextureVariantOverride, type, shouldConstruct: true);
1583+
}
1584+
1585+
private static void CloseGraphicCustomizationDialogOnAccept(Window __instance, ref Action action)
1586+
{
1587+
// The action is a synced method, and it handles closing the dialog. Since
1588+
// the method ends up being synced the closing does not happen. This will
1589+
// ensure that the dialog is closed when the accept button is pressed
1590+
// and the proper method will be synced as well.
1591+
var nonRefAction = action;
1592+
action = (() => __instance.Close()) + nonRefAction;
1593+
}
1594+
1595+
private static void CloseGraphicCustomizationWhenNoLongerValid(Window __instance, ThingComp ___comp, Pawn ___pawn)
1596+
{
1597+
// Since the dialog doesn't pause, ensure we close it if the comp's parent
1598+
// or the pawn ever get destroyed, despawned, or end up on different maps.
1599+
if (MP.IsInMultiplayer && !IsGraphicCustomizationDialogValid(___comp, ___pawn))
1600+
__instance.Close();
1601+
}
1602+
1603+
private static bool CancelGraphicCustomizationExecutionIfNoLongerValid(ThingComp ___comp, Pawn ___pawn)
1604+
=> !MP.IsInMultiplayer || IsGraphicCustomizationDialogValid(___comp, ___pawn);
1605+
1606+
private static bool IsGraphicCustomizationDialogValid(ThingComp comp, Pawn pawn)
1607+
=> comp.parent != null && pawn != null &&
1608+
comp.parent.Spawned && pawn.Spawned &&
1609+
!comp.parent.Destroyed && !pawn.Destroyed &&
1610+
!pawn.Dead && comp.parent.Map == pawn.Map;
1611+
1612+
private static void SyncGraphicCustomizationDialog(SyncWorker sync, ref Window dialog)
1613+
{
1614+
ThingComp comp = null;
1615+
1616+
if (sync.isWriting)
1617+
{
1618+
sync.Write(graphicCustomizationCompField(dialog));
1619+
}
1620+
else
1621+
{
1622+
// Skip constructor since it has a bunch of initialization we don't care about.
1623+
dialog = (Window)FormatterServices.GetUninitializedObject(graphicCustomizationDialogType);
1624+
1625+
comp = sync.Read<ThingComp>();
1626+
graphicCustomizationCompField(dialog) = comp;
1627+
}
1628+
1629+
SyncGraphicCustomizationDialog(sync, ref dialog, comp);
1630+
}
1631+
1632+
internal static void SyncGraphicCustomizationDialog(SyncWorker sync, ref Window dialog, ThingComp graphicCustomizationComp)
1633+
{
1634+
if (sync.isWriting)
1635+
{
1636+
sync.Write(graphicCustomizationPawnField(dialog));
1637+
sync.Write(graphicCustomizationCurrentVariantsField(dialog), variantsListType);
1638+
sync.Write(graphicCustomizationCurrentNameField(dialog));
1639+
}
1640+
else
1641+
{
1642+
graphicCustomizationGeneratedNamesCompField(dialog) = graphicCustomizationComp?.parent.GetComp<CompGeneratedNames>();
1643+
graphicCustomizationPawnField(dialog) = sync.Read<Pawn>();
1644+
graphicCustomizationCurrentVariantsField(dialog) = sync.Read<IList>(variantsListType);
1645+
graphicCustomizationCurrentNameField(dialog) = sync.Read<string>();
1646+
}
1647+
}
1648+
1649+
private static void SyncTextureVariant(SyncWorker sync, ref object variant)
1650+
{
1651+
if (sync.isWriting)
1652+
{
1653+
if (variant == null)
1654+
{
1655+
sync.Write(false);
1656+
return;
1657+
}
1658+
1659+
sync.Write(true);
1660+
1661+
sync.Write(textureVariantTexNameField(variant));
1662+
sync.Write(textureVariantTextureField(variant));
1663+
sync.Write(textureVariantOutlineField(variant));
1664+
sync.Write(textureVariantTextureVariantOverrideField(variant), textureVariantOverrideType);
1665+
sync.Write(textureVariantChanceOverrideField(variant));
1666+
}
1667+
else if (sync.Read<bool>())
1668+
{
1669+
textureVariantTexNameField(variant) = sync.Read<string>();
1670+
textureVariantTextureField(variant) = sync.Read<string>();
1671+
textureVariantOutlineField(variant) = sync.Read<string>();
1672+
textureVariantTextureVariantOverrideField(variant) = sync.Read<object>(textureVariantOverrideType);
1673+
textureVariantChanceOverrideField(variant) = sync.Read<float>();
1674+
}
1675+
}
1676+
1677+
private static void SyncTextureVariantOverride(SyncWorker sync, ref object variantOverride)
1678+
{
1679+
if (sync.isWriting)
1680+
{
1681+
if (variantOverride == null)
1682+
{
1683+
sync.Write(false);
1684+
return;
1685+
}
1686+
1687+
sync.Write(true);
1688+
sync.Write(textureVariantOverrideChanceField(variantOverride));
1689+
sync.Write(textureVariantOverrideGroupNameField(variantOverride));
1690+
sync.Write(textureVariantOverrideTexNameField(variantOverride));
1691+
}
1692+
else if (sync.Read<bool>())
1693+
{
1694+
textureVariantOverrideChanceField(variantOverride) = sync.Read<float>();
1695+
textureVariantOverrideGroupNameField(variantOverride) = sync.Read<string>();
1696+
textureVariantOverrideTexNameField(variantOverride) = sync.Read<string>();
1697+
}
1698+
}
1699+
1700+
#endregion
15241701
}
15251702
}

0 commit comments

Comments
 (0)