summaryrefslogtreecommitdiff
path: root/Parameter.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Parameter.cs')
-rw-r--r--Parameter.cs950
1 files changed, 950 insertions, 0 deletions
diff --git a/Parameter.cs b/Parameter.cs
new file mode 100644
index 0000000..db564a3
--- /dev/null
+++ b/Parameter.cs
@@ -0,0 +1,950 @@
+using System.Numerics;
+
+using ImGuiNET;
+using SharpPluginLoader.Core.Memory;
+
+namespace WorldTuningTool
+{
+ using static Utility;
+ using static PFuncs;
+
+ public enum ParameterType
+ {
+ BOOL,
+ FLAG,
+ BYTE,
+ INT,
+ ALIGNED_INT,
+ HEX,
+ BITS,
+ COLOR,
+ COLOR_FACTOR,
+ FLOAT,
+ VECTOR3,
+ VECTOR2,
+ VECTOR4,
+ PATCH_FLOAT,
+ SHADOW_RESOLUTION
+ }
+
+ public static class ParameterFlags
+ {
+ public const int ViewOnly = 1;
+ }
+
+ public static class PFuncs
+ {
+ public static bool IgnoreMinMax = false;
+
+ public static bool InTransition = false;
+ public static List<Parameter> DirtyParams = new();
+ public static void BeginTransition()
+ {
+ Assert(!InTransition && DirtyParams.Count == 0);
+ InTransition = true;
+ }
+ public static void EndTransition()
+ {
+ Assert(InTransition);
+ InTransition = false;
+ foreach (Parameter param in DirtyParams)
+ {
+ param.Resolve();
+ }
+ DirtyParams.Clear();
+ }
+
+ public static Action<int>? HandleShadowResolution = null;
+ public static Action<Parameter>? DrawStageOverrideTooltip = null;
+ public static Action<Parameter>? DrawExtraButtons = null;
+
+ public static Parameter<T> P<T>(string name, nint offset, ParameterType type, (T? min, T? max, T? step) m = default) where T : unmanaged
+ {
+ return new Parameter<T>(name, offset, 0, type, m.min, m.max, m.step, 0);
+ }
+
+ public static Parameter<T> P<T>(string name, long offset, ParameterType type, (T? min, T? max, T? step) m = default) where T : unmanaged
+ {
+ return new Parameter<T>(name, (nint)offset, 0, type, m.min, m.max, m.step, 0);
+ }
+
+ public static Parameter<T> V<T>(string name, nint offset, ParameterType type, (T? min, T? max, T? step) m = default) where T : unmanaged
+ {
+ return new Parameter<T>(name, offset, 0, type, m.min, m.max, m.step, ParameterFlags.ViewOnly);
+ }
+
+ public static Parameter<T> M<T>(string name, nint offset, ParameterType type, int mask, (T? min, T? max, T? step) m = default) where T : unmanaged
+ {
+ return new Parameter<T>(name, offset, mask, type, m.min, m.max, m.step, 0);
+ }
+
+ public static (float?, float?, float?) pStep(float step) { return (null, null, step); }
+ public static (float?, float?, float?) pMin(float min) { return (min, null, null); }
+ public static (float?, float?, float?) pMax(float max) { return (null, max, null); }
+ public static (float?, float?, float?) pMinStep(float min, float step) { return (min, null, step); }
+ public static (float?, float?, float?) pMaxStep(float max, float step) { return (null, max, step); }
+ public static (float?, float?, float?) pMinMaxStep(float min, float max, float step) { return (min, max, step); }
+ public static (int?, int?, int?) pStep(int step) { return (null, null, step); }
+ public static (int?, int?, int?) pMin(int min) { return (min, null, null); }
+ public static (int?, int?, int?) pMax(int max) { return (null, max, null); }
+ public static (int?, int?, int?) pMinStep(int min, int step) { return (min, null, step); }
+ public static (int?, int?, int?) pMaxStep(int max, int step) { return (null, max, step); }
+ public static (int?, int?, int?) pMinMaxStep(int min, int max, int step) { return (min, max, step); }
+ public static (byte?, byte?, byte?) pStep(byte step) { return (null, null, step); }
+ }
+
+ public abstract class Parameter
+ {
+ public string Name;
+ private string hiddenName;
+ public ParameterType Type;
+
+ private nint offset;
+ private int mask;
+ private nint lastAddr = 0x0;
+ private bool overrideValue = false;
+ private bool overrideForStage = false;
+ private bool overrideWasOn = false;
+
+ private int flags;
+ public bool ViewOnly => (flags & ParameterFlags.ViewOnly) == ParameterFlags.ViewOnly;
+
+ protected float stepf, minf, maxf;
+ protected int step, min, max;
+
+ private bool fallbackUpdate = true;
+ private bool pendingWrite = false;
+ private Vector4 valueV = default;
+ private int valueInt = 0;
+ private Vector4 oValueV; // Original value.
+ private int oValueInt;
+
+ private bool pendingUpdate = true;
+ private bool queuedOverride = false;
+ private bool queuedValue = false;
+ private Vector4 qValueV; // Queued value.
+ private int qValueInt;
+
+ private bool bValue = false;
+ private Vector4 bValueV; // B value in A/B.
+ private int bValueInt;
+
+ public Parameter(string name, nint offset, int mask, ParameterType type, int flags)
+ {
+ Name = name;
+ hiddenName = "##" + Name;
+ Type = type;
+ switch (Type)
+ {
+ case ParameterType.FLAG:
+ Assert(mask != 0);
+ break;
+ case ParameterType.PATCH_FLOAT:
+ valueV.X = Get<float>(offset);
+ pendingUpdate = false;
+ break;
+ case ParameterType.SHADOW_RESOLUTION:
+ valueInt = 1;
+ pendingUpdate = false;
+ break;
+ }
+ this.offset = offset;
+ this.mask = mask;
+ this.flags = flags;
+ }
+
+ public bool PendingUpdate()
+ {
+ return pendingUpdate;
+ }
+
+ public (Vector4, int) GetValue()
+ {
+ return (valueV, valueInt);
+ }
+
+ public int GetMask()
+ {
+ return mask;
+ }
+
+ public void Resolve()
+ {
+ Assert(!InTransition && pendingWrite);
+ pendingWrite = false;
+ maybeWriteCurrentValue();
+ }
+
+ private void maybeWriteCurrentValue()
+ {
+ if (InTransition)
+ {
+ if (!DirtyParams.Contains(this))
+ {
+ pendingWrite = true;
+ DirtyParams.Add(this);
+ }
+ else
+ {
+ Assert(pendingWrite);
+ }
+ return;
+ }
+ Assert(!pendingWrite);
+ switch (Type)
+ {
+ case ParameterType.PATCH_FLOAT:
+ new Patch(offset, BitConverter.GetBytes(valueV.X)).Enable();
+ break;
+ case ParameterType.SHADOW_RESOLUTION:
+ if (HandleShadowResolution != null)
+ {
+ HandleShadowResolution(valueInt);
+ }
+ break;
+ }
+ if (lastAddr == 0x0)
+ {
+ return;
+ }
+ switch (Type)
+ {
+ case ParameterType.BOOL:
+ case ParameterType.BYTE:
+ GetRef<byte>(lastAddr, offset) = (byte)valueInt;
+ break;
+ case ParameterType.FLAG:
+ {
+ ref int i1 = ref GetRef<int>(lastAddr, offset);
+ if (valueInt != 0)
+ {
+ i1 |= mask;
+ }
+ else
+ {
+ i1 &= ~mask;
+ }
+ break;
+ }
+ case ParameterType.INT:
+ case ParameterType.ALIGNED_INT:
+ case ParameterType.HEX:
+ case ParameterType.BITS:
+ case ParameterType.COLOR:
+ {
+ ref int i1 = ref GetRef<int>(lastAddr, offset);
+ if (mask != 0)
+ {
+ i1 = (i1 & ~mask) | ((valueInt << BitIndex(mask)) & mask);
+ }
+ else
+ {
+ i1 = valueInt;
+ }
+ break;
+ }
+ case ParameterType.FLOAT:
+ GetRef<float>(lastAddr, offset) = valueV.X;
+ break;
+ case ParameterType.VECTOR2:
+ GetRef<Vector2>(lastAddr, offset) = new(valueV.X, valueV.Y);
+ break;
+ case ParameterType.VECTOR3:
+ case ParameterType.COLOR_FACTOR:
+ GetRef<Vector3>(lastAddr, offset) = new(valueV.X, valueV.Y, valueV.Z);
+ break;
+ case ParameterType.VECTOR4:
+ GetRef<Vector4>(lastAddr, offset) = valueV;
+ break;
+ }
+ }
+
+ public void ForceOverrideOn()
+ {
+ if (!queuedOverride && !overrideValue)
+ {
+ OverrideOn(false);
+ }
+ }
+
+ public void ForceOverrideOff()
+ {
+ if (queuedOverride || overrideValue)
+ {
+ OverrideOff();
+ }
+ }
+
+ public void OverrideOn(bool forStage)
+ {
+ if (pendingUpdate)
+ {
+ if (queuedOverride)
+ {
+ Assert(!overrideWasOn && forStage);
+ overrideWasOn = true;
+ }
+ else
+ {
+ queuedOverride = true;
+ }
+ }
+ else
+ {
+ if (overrideValue)
+ {
+ Assert(!overrideWasOn && forStage);
+ overrideWasOn = true;
+ }
+ else
+ {
+ oValueV = valueV;
+ oValueInt = valueInt;
+ maybeWriteCurrentValue();
+ overrideValue = true;
+ }
+ }
+ overrideForStage = forStage;
+ }
+
+ public void OverrideOff()
+ {
+ if (queuedOverride)
+ {
+ if (overrideWasOn)
+ {
+ Assert(overrideForStage);
+ overrideWasOn = false;
+ }
+ else
+ {
+ queuedOverride = false;
+ }
+ }
+ else
+ {
+ Assert(overrideValue);
+ if (overrideWasOn)
+ {
+ Assert(overrideForStage);
+ overrideWasOn = false;
+ }
+ else
+ {
+ valueV = oValueV;
+ valueInt = oValueInt;
+ maybeWriteCurrentValue();
+ bValue = false;
+ overrideValue = false;
+ }
+ }
+ overrideForStage = false;
+ }
+
+ public void SetOverrideValue(Vector4 v4, int i1)
+ {
+ if (queuedOverride)
+ {
+ qValueV = v4;
+ qValueInt = i1;
+ queuedValue = true;
+ }
+ else
+ {
+ valueV = v4;
+ valueInt = i1;
+ maybeWriteCurrentValue();
+ }
+ }
+
+ public void Update(nint baseObject, bool fromHook = false)
+ {
+ lastAddr = baseObject;
+
+ if (baseObject == 0x0)
+ {
+ pendingUpdate = true;
+ return;
+ }
+
+ if (fromHook)
+ {
+ fallbackUpdate = false;
+ }
+ else if (!fallbackUpdate)
+ {
+ fallbackUpdate = true;
+ return;
+ }
+
+ // Trying to track changes to the original value is a "best effort"
+ // approach because it can't work if the in-game value is updated
+ // to the same value as a current override.
+ switch (Type)
+ {
+ case ParameterType.BOOL:
+ case ParameterType.BYTE:
+ {
+ ref byte b1 = ref GetRef<byte>(baseObject, offset);
+ byte b = b1;
+ if (overrideValue)
+ {
+ if (b != (byte)valueInt)
+ {
+ oValueInt = b;
+ }
+ b1 = (byte)valueInt;
+ }
+ else
+ {
+ valueInt = b;
+ }
+ break;
+ }
+ case ParameterType.FLAG:
+ {
+ ref int i1 = ref GetRef<int>(baseObject, offset);
+ int b1 = ByteFlag((i1 & mask) == mask);
+ if (overrideValue)
+ {
+ if (b1 != valueInt)
+ {
+ oValueInt = b1;
+ }
+ if (valueInt != 0)
+ {
+ i1 |= mask;
+ }
+ else
+ {
+ i1 &= ~mask;
+ }
+ }
+ else
+ {
+ valueInt = b1;
+ }
+ break;
+ }
+ case ParameterType.INT:
+ case ParameterType.ALIGNED_INT:
+ case ParameterType.HEX:
+ case ParameterType.BITS:
+ case ParameterType.COLOR:
+ {
+ ref int i1 = ref GetRef<int>(baseObject, offset);
+ int m = i1;
+ if (mask != 0)
+ {
+ m = (m & mask) >> BitIndex(mask);
+ }
+ if (overrideValue)
+ {
+ if (m != valueInt)
+ {
+ oValueInt = m;
+ }
+ if (mask != 0)
+ {
+ i1 = (i1 & ~mask) | ((valueInt << BitIndex(mask)) & mask);
+ }
+ else
+ {
+ i1 = valueInt;
+ }
+ }
+ else
+ {
+ valueInt = m;
+ }
+ break;
+ }
+ case ParameterType.FLOAT:
+ {
+ ref float f1 = ref GetRef<float>(baseObject, offset);
+ float f = f1;
+ if (overrideValue)
+ {
+ if (f != valueV.X)
+ {
+ oValueV.X = f;
+ }
+ f1 = valueV.X;
+ }
+ else
+ {
+ valueV.X = f;
+ }
+ break;
+ }
+ case ParameterType.VECTOR2:
+ {
+ ref Vector2 v2 = ref GetRef<Vector2>(baseObject, offset);
+ Vector2 v = v2;
+ if (overrideValue)
+ {
+ Vector2 valueV2 = new(valueV.X, valueV.Y);
+ if (v != valueV2)
+ {
+ oValueV = new Vector4(v.X, v.Y, 0.0f, 0.0f);
+ }
+ v2 = valueV2;
+ }
+ else
+ {
+ valueV = new Vector4(v.X, v.Y, 0.0f, 0.0f);
+ }
+ break;
+ }
+ case ParameterType.VECTOR3:
+ case ParameterType.COLOR_FACTOR:
+ {
+ ref Vector3 v3 = ref GetRef<Vector3>(baseObject, offset);
+ Vector3 v = v3;
+ if (overrideValue)
+ {
+ Vector3 valueV3 = new(valueV.X, valueV.Y, valueV.Z);
+ if (v != valueV3)
+ {
+ oValueV = new Vector4(v.X, v.Y, v.Z, 0.0f);
+ }
+ v3 = valueV3;
+ }
+ else
+ {
+ valueV = new Vector4(v.X, v.Y, v.Z, 0.0f);
+ }
+ break;
+ }
+ case ParameterType.VECTOR4:
+ {
+ ref Vector4 v4 = ref GetRef<Vector4>(baseObject, offset);
+ Vector4 v = v4;
+ if (overrideValue)
+ {
+ if (v != valueV)
+ {
+ oValueV = v;
+ }
+ v4 = valueV;
+ }
+ else
+ {
+ valueV = v;
+ }
+ break;
+ }
+ }
+
+ if (pendingUpdate)
+ {
+ pendingUpdate = false;
+ if (queuedOverride)
+ {
+ Assert(!overrideValue);
+ OverrideOn(overrideForStage);
+ if (queuedValue)
+ {
+ valueV = qValueV;
+ valueInt = qValueInt;
+ maybeWriteCurrentValue();
+ queuedValue = false;
+ }
+ queuedOverride = false;
+ }
+ }
+ }
+
+ public void AdjustValue(ref Vector4 v4, ref int i1)
+ {
+ if (Type == ParameterType.SHADOW_RESOLUTION) // Hard limit.
+ {
+#if OVERSIZED_SHADOW_MAP
+ i1 = Math.Clamp(i1, 1, 6);
+#else
+ i1 = Math.Clamp(i1, 1, 5);
+#endif
+ }
+ else if (!IgnoreMinMax)
+ {
+ switch (Type)
+ {
+ case ParameterType.BOOL:
+ case ParameterType.FLAG:
+ case ParameterType.BYTE:
+ case ParameterType.INT:
+ case ParameterType.HEX:
+ case ParameterType.BITS:
+ i1 = Math.Clamp(i1, min, max);
+ break;
+ case ParameterType.ALIGNED_INT:
+ i1 = Math.Clamp((i1 + (step - 1))&~(step - 1), min, max);
+ break;
+ case ParameterType.FLOAT:
+ case ParameterType.PATCH_FLOAT:
+ case ParameterType.VECTOR2:
+ case ParameterType.VECTOR3:
+ case ParameterType.COLOR_FACTOR:
+ case ParameterType.VECTOR4:
+ v4 = new Vector4(
+ Math.Clamp(v4.X, minf, maxf),
+ Math.Clamp(v4.Y, minf, maxf),
+ Math.Clamp(v4.Z, minf, maxf),
+ Math.Clamp(v4.W, minf, maxf));
+ break;
+ }
+ }
+ }
+
+ public bool DrawValue(ref Vector4 v4, ref int i1, float width, bool drawLabel)
+ {
+ bool valueChanged = false;
+ string label = drawLabel ? Name : hiddenName;
+ float displayMinf = IgnoreMinMax ? 0.0f : minf;
+ float displayMaxf = IgnoreMinMax ? 0.0f : maxf;
+ switch (Type)
+ {
+ case ParameterType.BOOL:
+ case ParameterType.FLAG:
+ {
+ bool b = i1 != 0;
+ if (ImGui.Checkbox(label, ref b))
+ {
+ i1 = ByteFlag(b);
+ valueChanged = true;
+ }
+ break;
+ }
+ case ParameterType.BYTE:
+ {
+ ImGuiInputTextFlags flags = ImGuiInputTextFlags.EnterReturnsTrue;
+ ImGui.SetNextItemWidth(width * defaultParamWidth);
+ if (ImGui.InputInt(label, ref i1, step, 0, flags))
+ {
+ valueChanged = true;
+ }
+ break;
+ }
+ case ParameterType.INT:
+ case ParameterType.ALIGNED_INT:
+ case ParameterType.HEX:
+ case ParameterType.SHADOW_RESOLUTION:
+ {
+ ImGuiInputTextFlags flags = ImGuiInputTextFlags.EnterReturnsTrue;
+ if (Type == ParameterType.HEX)
+ {
+ flags |= ImGuiInputTextFlags.CharsHexadecimal;
+ }
+ ImGui.SetNextItemWidth(width * defaultParamWidth);
+ if (ImGui.InputInt(label, ref i1, step, 0, flags))
+ {
+ valueChanged = true;
+ }
+ break;
+ }
+ case ParameterType.BITS:
+ {
+ for (int i = 0; i < 32; i++)
+ {
+ int bit = (1 << i);
+ if ((mask & bit) == bit)
+ {
+ bool set = (i1 & bit) == bit;
+ if (ImGui.Checkbox($"##Bit{i}", ref set))
+ {
+ if (set)
+ {
+ i1 |= bit;
+ }
+ else
+ {
+ i1 &= ~bit;
+ }
+ valueChanged = true;
+ }
+ ImGui.SameLine();
+ }
+ }
+ ImGui.Text(label);
+ break;
+ }
+ case ParameterType.COLOR:
+ {
+ Vector4 colorV = ColorVectorFromInt(i1);
+ ImGui.SetNextItemWidth(width * defaultParamWidth * 3.0f);
+ if (ImGui.ColorEdit4(label, ref colorV))
+ {
+ i1 = ColorVectorToInt(colorV);
+ valueChanged = true;
+ }
+ break;
+ }
+ case ParameterType.FLOAT:
+ case ParameterType.PATCH_FLOAT:
+ {
+ float f1 = v4.X;
+ ImGui.SetNextItemWidth(width * defaultParamWidth);
+ if (ImGui.DragFloat(label, ref f1, stepf, displayMinf, displayMaxf, "%.6f", ImGuiSliderFlags.NoRoundToFormat))
+ {
+ v4 = new Vector4(f1, 0.0f, 0.0f, 0.0f);
+ valueChanged = true;
+ }
+ break;
+ }
+ case ParameterType.VECTOR2:
+ {
+ Vector2 v2 = new(v4.X, v4.Y);
+ ImGui.SetNextItemWidth(width * defaultParamWidth * 2.0f);
+ if (ImGui.DragFloat2(label, ref v2, stepf, displayMinf, displayMaxf, "%.5f", ImGuiSliderFlags.NoRoundToFormat))
+ {
+ v4 = new Vector4(v2.X, v2.Y, 0.0f, 0.0f);
+ valueChanged = true;
+ }
+ break;
+ }
+ case ParameterType.VECTOR3:
+ case ParameterType.COLOR_FACTOR:
+ {
+ Vector3 v3 = new(v4.X, v4.Y, v4.Z);
+ if (Type == ParameterType.COLOR_FACTOR)
+ {
+ Vector3 v3Color = v3;
+ // Only scale the color vector if max is unbound. Otherwise, assume that intensity and color are seperate.
+ if (maxf == Single.MaxValue)
+ {
+ float v3Max = MathF.Max(Math.Abs(v3.X), MathF.Max(Math.Abs(v3.Y), Math.Abs(v3.Z)));
+ if (v3Max != 0.0f)
+ {
+ // I believe a compromise is needed here. These are the options I can think of.
+ //v3Color /= v3Max; // Preview will be full "brightness" at low intensities.
+ v3Color = v3Color * (v3Color / v3Max); // Preview will converge towards white at high intensities.
+ }
+ }
+ ImGui.ColorEdit3(label, ref v3Color, ImGuiColorEditFlags.NoPicker | ImGuiColorEditFlags.NoLabel | ImGuiColorEditFlags.NoInputs);
+ ImGui.SameLine();
+ }
+ ImGui.SetNextItemWidth(width * defaultParamWidth * 3.0f);
+ if (ImGui.DragFloat3(label, ref v3, stepf, displayMinf, displayMaxf, "%.5f", ImGuiSliderFlags.NoRoundToFormat))
+ {
+ v4 = new Vector4(v3.X, v3.Y, v3.Z, 0.0f);
+ valueChanged = true;
+ }
+ break;
+ }
+ case ParameterType.VECTOR4:
+ {
+ ImGui.SetNextItemWidth(width * defaultParamWidth * 4.0f);
+ if (ImGui.DragFloat4(label, ref v4, stepf, displayMinf, displayMaxf, "%.5f", ImGuiSliderFlags.NoRoundToFormat))
+ {
+ valueChanged = true;
+ }
+ break;
+ }
+ }
+ if (valueChanged)
+ {
+ AdjustValue(ref v4, ref i1);
+ }
+ return valueChanged;
+ }
+
+ public void DrawReferenceValue(string name, Vector4 v4, int i1)
+ {
+ ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * fadeAlphaFactor);
+ switch (Type)
+ {
+ case ParameterType.BOOL:
+ case ParameterType.FLAG:
+ ImGui.Text($"({name}: {i1 != 0})");
+ break;
+ case ParameterType.BYTE:
+ case ParameterType.INT:
+ case ParameterType.ALIGNED_INT:
+ case ParameterType.SHADOW_RESOLUTION:
+ ImGui.Text($"({name}: {i1})");
+ break;
+ case ParameterType.HEX:
+ case ParameterType.BITS:
+ case ParameterType.COLOR:
+ ImGui.Text($"({name}: {i1:X})");
+ break;
+ case ParameterType.FLOAT:
+ case ParameterType.PATCH_FLOAT:
+ ImGui.Text($"({name}: {v4.X:0.000000})");
+ break;
+ case ParameterType.VECTOR2:
+ ImGui.Text($"({name}: {v4.X:0.00000}, {v4.Y:0.00000})");
+ break;
+ case ParameterType.VECTOR3:
+ case ParameterType.COLOR_FACTOR:
+ ImGui.Text($"({name}: {v4.X:0.00000}, {v4.Y:0.00000}, {v4.Z:0.00000})");
+ break;
+ case ParameterType.VECTOR4:
+ ImGui.Text($"({name}: {v4.X:0.00000}, {v4.Y:0.00000}, {v4.Z:0.00000}, {v4.W:0.00000})");
+ break;
+ }
+ ImGui.PopStyleVar();
+ }
+
+ public void Draw(float width, bool assumeOverride = false)
+ {
+ Draw(ref valueV, ref valueInt, width, assumeOverride);
+ }
+
+ public void Draw(ref Vector4 v4, ref int i1, float width, bool assumeOverride)
+ {
+ ImGui.PushID(Name);
+
+ if (ViewOnly)
+ {
+ ImGui.SetCursorPos(ImGui.GetCursorPos() + new Vector2(ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.X, 0.0f));
+ }
+ else if (!assumeOverride)
+ {
+ if (overrideForStage)
+ {
+ ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true);
+ ImGui.PushItemFlag(ImGuiItemFlags.MixedValue, true);
+ }
+ // If OverrideOn() were to set queuedOverride instead of overrideValue, this parameter shouldn't be drawn.
+ Assert(!PendingUpdate());
+ bool toggleOverride = overrideValue;
+ if (ImGui.Checkbox("##Override", ref toggleOverride))
+ {
+ if (toggleOverride)
+ {
+ OverrideOn(false);
+ }
+ else
+ {
+ OverrideOff();
+ }
+ }
+ if (ImGui.BeginItemTooltip())
+ {
+ if (overrideForStage && DrawStageOverrideTooltip != null)
+ {
+ DrawStageOverrideTooltip(this);
+ }
+ else if (overrideValue)
+ {
+ ImGui.Text("Set");
+ }
+ else
+ {
+ ImGui.Text("Set override");
+ }
+ ImGui.EndTooltip();
+ }
+ if (overrideForStage)
+ {
+ ImGui.PopItemFlag();
+ ImGui.PopItemFlag();
+ }
+ ImGui.SameLine();
+ }
+
+ if (!overrideValue)
+ {
+ ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true);
+ }
+ if (DrawValue(ref valueV, ref valueInt, width, !bValue))
+ {
+ maybeWriteCurrentValue();
+ }
+ if (!overrideValue)
+ {
+ ImGui.PopItemFlag();
+ }
+
+ bool showButtons = !assumeOverride && overrideValue && !pendingUpdate;
+ if (showButtons)
+ {
+ ImGui.SameLine();
+ if (!bValue)
+ {
+ if (ImGui.Button("B"))
+ {
+ bValueV = oValueV;
+ bValueInt = oValueInt;
+ bValue = true;
+ }
+ }
+ else
+ {
+ if (ImGui.Button("A/B"))
+ {
+ (bValueV, valueV) = (valueV, bValueV);
+ (bValueInt, valueInt) = (valueInt, bValueInt);
+ maybeWriteCurrentValue();
+ }
+ ImGui.SameLine();
+ DrawValue(ref bValueV, ref bValueInt, width, true);
+ ImGui.SameLine();
+ if (ImGui.Button("< B"))
+ {
+ bValue = false;
+ }
+ }
+ if (DrawExtraButtons != null)
+ {
+ DrawExtraButtons(this);
+ }
+ ImGui.SameLine();
+ DrawReferenceValue("Original", oValueV, oValueInt);
+ }
+
+ ImGui.PopID();
+ }
+ }
+
+ public class Parameter<T> : Parameter where T : unmanaged
+ {
+ public Parameter(string name, nint offset, int mask, ParameterType type, T? min, T? max, T? step, int flags) : base(name, offset, mask, type, flags)
+ {
+ if (typeof(T) == typeof(float))
+ {
+ Assert(type == ParameterType.FLOAT ||
+ type == ParameterType.VECTOR2 ||
+ type == ParameterType.VECTOR3 ||
+ type == ParameterType.VECTOR4 ||
+ type == ParameterType.PATCH_FLOAT ||
+ type == ParameterType.COLOR_FACTOR);
+ this.stepf = (step != null) ? Convert.ToSingle(step) : 0.1f;
+ this.minf = (min != null) ? Convert.ToSingle(min) : -Single.MaxValue;
+ this.maxf = (max != null) ? Convert.ToSingle(max) : Single.MaxValue;
+ }
+ else
+ {
+ this.step = (step != null) ? Convert.ToInt32(step) : 0;
+ if (typeof(T) == typeof(int))
+ {
+ Assert(type == ParameterType.INT ||
+ type == ParameterType.ALIGNED_INT ||
+ type == ParameterType.HEX ||
+ type == ParameterType.BITS ||
+ type == ParameterType.COLOR ||
+ type == ParameterType.SHADOW_RESOLUTION);
+ this.min = (min != null) ? Convert.ToInt32(min) : Int32.MinValue;
+ this.max = (max != null) ? Convert.ToInt32(max) : Int32.MaxValue;
+ }
+ else if (typeof(T) == typeof(byte))
+ {
+ Assert(type == ParameterType.BYTE);
+ this.min = (min != null) ? Convert.ToInt32(min) : 0x0;
+ this.max = (max != null) ? Convert.ToInt32(max) : 0xFF;
+ }
+ else if (typeof(T) == typeof(bool))
+ {
+ Assert(type == ParameterType.BOOL || type == ParameterType.FLAG);
+ this.min = 0;
+ this.max = 1;
+ }
+ else
+ {
+ Assert();
+ }
+ }
+ }
+ }
+}