Extract generic object field multiplier logic to separate file
This commit is contained in:
165
Projects/TerraTech/TerraTech/ObjectFieldMultiplier.cs
Normal file
165
Projects/TerraTech/TerraTech/ObjectFieldMultiplier.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BepInEx.Configuration;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace TerraTech {
|
||||
public class MultipliedField<TObject, TValue> {
|
||||
private readonly string fieldName;
|
||||
private readonly ConfigEntry<TValue> multiplier;
|
||||
private readonly Traverse parentTraverse;
|
||||
private TValue originalValue;
|
||||
|
||||
public MultipliedField(string fieldName, ConfigEntry<TValue> multiplier, Traverse parentTraverse) {
|
||||
this.fieldName = fieldName;
|
||||
this.multiplier = multiplier;
|
||||
this.parentTraverse = parentTraverse;
|
||||
|
||||
if (!parentTraverse.Field(fieldName).FieldExists()) {
|
||||
throw new ArgumentException(
|
||||
string.Format("Field {0} does not exist on {1}", fieldName, parentTraverse.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
public TValue GetValue() {
|
||||
var value = parentTraverse.Field(fieldName).GetValue();
|
||||
if (value == null)
|
||||
throw new InvalidOperationException(string.Format("Field {0} returned null", fieldName));
|
||||
return (TValue)value;
|
||||
}
|
||||
|
||||
public void SetValue(TValue value) {
|
||||
parentTraverse.Field(fieldName).SetValue(value);
|
||||
var verifyValue = GetValue();
|
||||
if (!verifyValue.Equals(value))
|
||||
throw new InvalidOperationException(
|
||||
string.Format("Field {0} set to {1} but read back as {2}", fieldName, value, verifyValue));
|
||||
}
|
||||
|
||||
public void CaptureOriginal() {
|
||||
originalValue = GetValue();
|
||||
if (Main.debug.Value)
|
||||
Console.WriteLine(string.Format("Captured original value for {0}: {1}", fieldName, originalValue));
|
||||
}
|
||||
|
||||
public void Apply() {
|
||||
try {
|
||||
dynamic originalDynamic = originalValue;
|
||||
dynamic multiplierDynamic = multiplier.Value;
|
||||
var newValue = (TValue)(originalDynamic * multiplierDynamic);
|
||||
|
||||
if (Main.debug.Value)
|
||||
Console.WriteLine(string.Format("Applying to {0}: {1} * {2} = {3}", fieldName, originalValue,
|
||||
multiplier.Value, newValue));
|
||||
|
||||
SetValue(newValue);
|
||||
} catch (Exception e) {
|
||||
throw new InvalidOperationException(string.Format("Failed to apply multiplication to {0}", fieldName),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
public void Restore() {
|
||||
if (Main.debug.Value)
|
||||
Console.WriteLine(string.Format("Restoring {0} to original value: {1}", fieldName, originalValue));
|
||||
SetValue(originalValue);
|
||||
}
|
||||
|
||||
public void LogValue(string prefix) {
|
||||
if (!Main.debug.Value)
|
||||
return;
|
||||
var currentValue = GetValue();
|
||||
Console.WriteLine(
|
||||
$"{prefix} {parentTraverse}; {fieldName}: {currentValue} (original: {originalValue}, multiplier: {multiplier.Value})");
|
||||
}
|
||||
}
|
||||
|
||||
public class MultipliedObject<T> {
|
||||
private readonly Traverse objectTraverse;
|
||||
private readonly List<MultipliedField<T, float>> fields = new List<MultipliedField<T, float>>();
|
||||
|
||||
public MultipliedObject(T instance) {
|
||||
objectTraverse = Traverse.Create(instance);
|
||||
}
|
||||
|
||||
public void AddField(string fieldName, ConfigEntry<float> multiplier) {
|
||||
fields.Add(new MultipliedField<T, float>(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<T>
|
||||
where T : class {
|
||||
private readonly Dictionary<T, MultipliedObject<T>> managedObjects = new Dictionary<T, MultipliedObject<T>>();
|
||||
private readonly Action<MultipliedObject<T>> configureObject;
|
||||
|
||||
public MultipliedObjectManager(Action<MultipliedObject<T>> configureObject) {
|
||||
this.configureObject = configureObject;
|
||||
}
|
||||
|
||||
public void OnObjectAttached(T instance) {
|
||||
if (!managedObjects.ContainsKey(instance)) {
|
||||
if (Main.debug.Value)
|
||||
Console.WriteLine(string.Format("{0}.OnAttached", typeof(T).Name));
|
||||
|
||||
var multipliedObject = new MultipliedObject<T>(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()
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user