diff --git a/Projects/TerraTech/TerraTech/ModuleShieldGeneratorManager.cs b/Projects/TerraTech/TerraTech/ModuleShieldGeneratorManager.cs index 8e928d1..4b68518 100644 --- a/Projects/TerraTech/TerraTech/ModuleShieldGeneratorManager.cs +++ b/Projects/TerraTech/TerraTech/ModuleShieldGeneratorManager.cs @@ -4,126 +4,130 @@ using BepInEx.Configuration; using HarmonyLib; namespace TerraTech { + public class MultipliedField { + private readonly string fieldName; + private readonly ConfigEntry multiplier; + private readonly Traverse parentTraverse; + private TValue originalValue; + + public MultipliedField(string fieldName, ConfigEntry multiplier, Traverse parentTraverse) { + this.fieldName = fieldName; + this.multiplier = multiplier; + this.parentTraverse = parentTraverse; + } + + public TValue GetValue() => (TValue)parentTraverse.Field(fieldName).GetValue(); + public void SetValue(TValue value) => parentTraverse.Field(fieldName).SetValue(value); + public void CaptureOriginal() => originalValue = GetValue(); + public void Restore() => SetValue(originalValue); + + public void Apply() { + dynamic originalDynamic = originalValue; + dynamic multiplierDynamic = multiplier.Value; + SetValue((TValue)(originalDynamic * multiplierDynamic)); + } + + public void LogValue(string prefix) { + if (!Main.debug.Value) + return; + Console.WriteLine("{0} {1}; {2}: {3}", prefix, parentTraverse.ToString(), fieldName, GetValue()); + } + } + + public class MultipliedObject { + private readonly Traverse objectTraverse; + private readonly List> fields = new List>(); + + public MultipliedObject(T instance) => objectTraverse = Traverse.Create(instance); + public void AddField(string fieldName, ConfigEntry multiplier) => + fields.Add(new MultipliedField(fieldName, multiplier, objectTraverse)); + public void CaptureFrom() => fields.ForEach(f => f.CaptureOriginal()); + public void ApplyTo() => fields.ForEach(f => f.Apply()); + public void RestoreTo() => fields.ForEach(f => f.Restore()); + public void LogValues(string prefix) { + if (!Main.debug.Value) + return; + fields.ForEach(f => f.LogValue(prefix)); + } + } + + public class MultipliedObjectManager + where T : class { + private readonly Dictionary> managedObjects = new Dictionary>(); + private readonly Action> configureObject; + + public MultipliedObjectManager(Action> configureObject) { + this.configureObject = configureObject; + } + + public void OnObjectAttached(T instance) { + if (!managedObjects.ContainsKey(instance)) { + if (Main.debug.Value) + Console.WriteLine($"{typeof(T).Name}.OnAttached"); + + var multipliedObject = new MultipliedObject(instance); + configureObject(multipliedObject); + multipliedObject.CaptureFrom(); + managedObjects.Add(instance, multipliedObject); + multipliedObject.LogValues("Patching"); + + ApplyTo(instance); + multipliedObject.LogValues("Patched"); + } + } + + public void OnObjectDetached(T instance) { + if (managedObjects.TryGetValue(instance, out var multipliedObject)) { + if (Main.debug.Value) { + Console.WriteLine($"{typeof(T).Name}.OnDetaching"); + multipliedObject.LogValues("Restoring"); + } + + RestoreTo(instance); + multipliedObject.LogValues("Restored"); + managedObjects.Remove(instance); + } + } + + public void ApplyAll() { + if (Main.debug.Value) + Console.WriteLine($"Modifying {managedObjects.Count} {typeof(T).Name}"); + foreach (var instance in managedObjects.Keys) { + RestoreTo(instance); + ApplyTo(instance); + } + } + + private void ApplyTo(T instance) { + if (managedObjects.TryGetValue(instance, out var obj)) + obj.ApplyTo(); + } + + private void RestoreTo(T instance) { + if (managedObjects.TryGetValue(instance, out var obj)) + obj.RestoreTo(); + } + } + [HarmonyPatch] public class ModuleShieldGeneratorManager { - private class MultipliedField { - private readonly string fieldName; - private readonly ConfigEntry multiplier; - private readonly Traverse parentTraverse; - private TValue originalValue; + private static readonly MultipliedObjectManager manager = + new MultipliedObjectManager(ConfigureShieldGenerator); - public MultipliedField(string fieldName, ConfigEntry multiplier, Traverse parentTraverse) { - this.fieldName = fieldName; - this.multiplier = multiplier; - this.parentTraverse = parentTraverse; - } - - public TValue GetValue() { - return (TValue)parentTraverse.Field(fieldName).GetValue(); - } - - public void SetValue(TValue value) { - parentTraverse.Field(fieldName).SetValue(value); - } - - public void CaptureOriginal() { - originalValue = GetValue(); - } - - public void Apply() { - // This assumes TValue implements multiplication with multiplier.Value - // For float, this would be originalValue * multiplier.Value - dynamic originalDynamic = originalValue; - dynamic multiplierDynamic = multiplier.Value; - SetValue((TValue)(originalDynamic * multiplierDynamic)); - } - - public void Restore() { - SetValue(originalValue); - } - - public void LogValue(string prefix) { - if (!Main.debug.Value) - return; - Console.WriteLine("{0} {1}; {2}: {3}", prefix, parentTraverse.ToString(), fieldName, GetValue()); - } + private static void ConfigureShieldGenerator(MultipliedObject obj) { + obj.AddField("m_HealingHeartbeatInterval", Main.shieldHeartbeatIntervalMultiplier); + obj.AddField("m_Radius", Main.shieldRadiusMultiplier); + obj.AddField("m_PowerUpDelay", Main.powerUpDelayMultiplier); } - private class MultipliedObject { - private readonly Traverse objectTraverse; - private readonly List> fields = new List>(); - - public MultipliedObject(T instance) objectTraverse = Traverse.Create(instance); - public void AddField(string fieldName, ConfigEntry multiplier) fields.Add( - new MultipliedField(fieldName, multiplier, objectTraverse)); - public void CaptureFrom() foreach (var field in fields) field.CaptureOriginal(); - public void ApplyTo() foreach (var field in fields) field.Apply(); - public void RestoreTo() foreach (var field in fields) field.Restore(); - - public void LogValues(string prefix) { - if (!Main.debug.Value) - return; - foreach (var field in fields) field.LogValue(prefix); - } - } - - private static Dictionary> originalProperties = - new Dictionary>(); - [HarmonyPrefix] [HarmonyPatch(typeof(ModuleShieldGenerator), "OnAttached")] - static void PostfixCreate(ModuleShieldGenerator __instance) { - if (Main.debug.Value) - Console.WriteLine("ModuleShieldGenerator.OnAttached"); - - if (!originalProperties.ContainsKey(__instance)) { - var props = new MultipliedObject(__instance); - props.AddField("m_HealingHeartbeatInterval", Main.shieldHeartbeatIntervalMultiplier); - props.AddField("m_Radius", Main.shieldRadiusMultiplier); - props.AddField("m_PowerUpDelay", Main.powerUpDelayMultiplier); - - props.CaptureFrom(); - originalProperties.Add(__instance, props); - props.LogValues("Patching"); - } - - DoPatchSingle(__instance); - - if (Main.debug.Value) { - originalProperties[__instance].LogValues("Patched"); - } - } + static void PostfixCreate(ModuleShieldGenerator __instance) => manager.OnObjectAttached(__instance); [HarmonyPrefix] [HarmonyPatch(typeof(ModuleShieldGenerator), "OnDetaching")] - static void PostfixDestroy(ModuleShieldGenerator __instance) { - if (Main.debug.Value) { - Console.WriteLine("ModuleShieldGenerator.OnDetaching"); - originalProperties[__instance].LogValues("Restoring"); - } + static void PostfixDestroy(ModuleShieldGenerator __instance) => manager.OnObjectDetached(__instance); - DoRestoreSingle(__instance); - - if (Main.debug.Value) { - originalProperties[__instance].LogValues("Restored"); - } - - originalProperties.Remove(__instance); - } - - public static void DoPatch() { - if (Main.debug.Value) - Console.WriteLine("Modifying {0} ModuleShieldGenerator", originalProperties.Count); - foreach (var kvp in originalProperties) { - DoRestoreSingle(kvp.Key); - DoPatchSingle(kvp.Key); - } - } - - static void DoPatchSingle(ModuleShieldGenerator moduleShieldGenerator) if ( - originalProperties.TryGetValue(moduleShieldGenerator, out var props)) props.ApplyTo(); - - static void DoRestoreSingle(ModuleShieldGenerator moduleShieldGenerator) if ( - originalProperties.TryGetValue(moduleShieldGenerator, out var props)) props.RestoreTo(); + public static void DoPatch() => manager.ApplyAll(); } }