Major settings overhaul

This commit is contained in:
2026-01-03 13:10:26 +01:00
parent f42732dbc4
commit 8c9c3b3d94
17 changed files with 284 additions and 289 deletions

View File

@@ -7,8 +7,8 @@ namespace HighRollerClassic;
[Serializable]
public class Configuration : IPluginConfiguration
{
public PlayerManager Players { get; set; }= new();
public Settings Settings { get; set; } = new();
public PlayerManager Players { get; set; } = new();
public SettingsStructure.Settings Settings { get; set; } = new(null);
public int Version { get; set; } = 0;

View File

@@ -1,16 +0,0 @@
namespace HighRollerClassic.DataStructures;
public static class Misc
{
/// <summary>
/// Appends message if error par is true
/// </summary>
/// <param name="error">it will write the message if it's true</param>
/// <param name="errorMessage">the message we wish to write</param>
/// <param name="message">reference to the message</param>
public static void AppendMessage(bool error, string errorMessage, ref string? message)
{
if (!error) return;
message = message == null ? errorMessage : $"\n{errorMessage}";
}
}

View File

@@ -1,73 +0,0 @@
using System.Collections.Generic;
namespace HighRollerClassic.DataStructures;
public class Settings
{
/// <summary>
/// Whether developer options should be visible in the settings
/// </summary>
public bool devOptions = false;
/// <summary>
/// Maximum bet that will be allowed to set
/// </summary>
public uint? MaxBet { get; set; } = null;
/// <summary>
/// How much the bet will change when user adjusts their bet via +/- keys
/// </summary>
public uint? Step { get; set; } = null;
/// <summary>
/// Contains messages such as announcements etc.
/// </summary>
public List<MessageMacro> Macros { get; private set; } = new();
/// <summary>
/// Contains data about each multiplier, roll, etc.
/// </summary>
public List<SettingsRoll> Rolls { get; private set; } = new();
// todo we'll make this a new class because we have 3 roll methods already 'polluting' this class
/// <summary>
/// Adds roll
/// </summary>
/// <param name="newRoll">The roll 'collection' we wish to add</param>
/// <returns>returns error message if validations fails, null if it's successfully added</returns>
public string? AddRoll(SettingsRoll newRoll)
{
var individualValidation = ValidateIndividual(newRoll);
if (individualValidation != null) return individualValidation;
var fullValidation = ValidateRoll(newRoll);
if (fullValidation != null) return fullValidation;
Rolls.Add(newRoll);
return null;
}
public void RemoveRoll(SettingsRoll roll)
{
Rolls.Remove(roll);
}
/// <summary>
/// Validates new roll
/// </summary>
/// <param name="newRoll">The roll we wish to add</param>
/// <returns>Returns error message if validation fails, otherwise null string</returns>
private string? ValidateRoll(SettingsRoll newRoll)
{
string? message = null;
var rollExists = Rolls.Exists(p => p.Roll == newRoll.Roll);
Misc.AppendMessage(rollExists, "The roll already exists", ref message);
var multiplierExists = Rolls.Exists(p => p.Multiplier == newRoll.Multiplier);
Misc.AppendMessage(multiplierExists, "This multiplier already exists", ref message);
var colourExists = Rolls.Exists(p => p.Colour == newRoll.Colour);
Misc.AppendMessage(colourExists, "This colour already exists", ref message);
return message;
}
}

View File

@@ -1,45 +0,0 @@
using System.Numerics;
namespace HighRollerClassic.DataStructures;
/// <summary>
/// Stores individual 'Roll' setting (multiplier, roll, colour)
/// </summary>
/// <param name="Multiplier">the amount gil will get multiplied if roll interval is reached</param>
/// <param name="Roll">the amount that will manipulate multiplier, usually acquired by a player running /random</param>
/// <param name="Colour">each roll can be colour coded for better visibility</param>
public record SettingsRoll(uint? Multiplier, uint? Roll, Vector4? Colour)
{
private const uint MinRollInterval = 1;
private const uint MaxRollInterval = 999;
/// <summary>
/// Roll multiplier
/// </summary>
public uint? Multiplier { get; set; } = Multiplier;
/// <summary>
/// Roll itself
/// </summary>
public uint? Roll
{
get;
set
{
if (value is < MinRollInterval or > MaxRollInterval)
{
field = null;
return;
}
field = value;
}
} = Roll;
/// <summary>
/// Colour that will be used to highlight roll in history
/// </summary>
public Vector4? Colour { get; set; } = Colour;
public bool ValidInput => Multiplier.HasValue && Roll.HasValue;
}

View File

@@ -1,95 +0,0 @@
using System;
using System.Diagnostics;
using System.Numerics;
using FFXIVClientStructs.FFXIV.Component.GUI;
namespace HighRollerClassic.DataStructures;
/// <summary>
/// Tracks value and its validity and changes
/// </summary>
public class TrackedValue
{
private SettingValueType type;
private Configuration configuration;
private uint? Value { get;
set
{
SetValue(value);
field = value;
}
}
private Vector4? colourValue { get; set; }
public bool IsValid { get; private set; }
public bool HasChanged { get; private set; }
private const uint MinRoll = 1;
private const uint MaxRoll = 999;
private const uint MaxGil = 999_999_999;
private bool multiplierValid => Value is not null;
private bool rollValid => Value is >= MinRoll and <= MaxRoll;
private bool maxBetValid => Value is <= MaxGil;
private bool stepValid => CheckStepValid();
public TrackedValue(uint? value, SettingValueType type, Configuration configuration)
{
Initialise(type, configuration);
Value = value;
}
public TrackedValue(Vector4? value, SettingValueType type, Configuration configuration)
{
Initialise(type, configuration);
colourValue = value;
}
private void Initialise(SettingValueType type, Configuration configuration)
{
this.type = type;
this.configuration = configuration;
}
private bool CheckStepValid()
{
return Value <= configuration.Settings.MaxBet;
}
private void SetValue(uint? value)
{
bool? valid = null;
bool? change = null;
switch (type)
{
case SettingValueType.Multiplier:
valid = multiplierValid;
break;
case SettingValueType.Roll:
valid = rollValid;
break;
case SettingValueType.Colour:
valid = true;
break;
case SettingValueType.MaxBet:
valid = maxBetValid;
change = value == configuration.Settings.MaxBet;
break;
case SettingValueType.Step:
valid = stepValid;
change = value == configuration.Settings.Step;
break;
}
if (valid.HasValue) IsValid = valid.Value;
if (change.HasValue) HasChanged = change.Value;
}
private void SetColourValue(Vector4? value)
{
IsValid = true; // colour will always be valid because we set it from colour picker
HasChanged = true; // todo we have to check
}
}

View File

@@ -0,0 +1,71 @@
using System.Collections.Generic;
namespace HighRollerClassic.SettingsStructure;
public class RollsCollection
{
/// <summary>
/// Contains data about each multiplier, roll, etc.
/// </summary>
public List<SettingsRoll> Rolls { get; set; } = new();
public int Size => Rolls.Count;
/// <summary>
/// Adds roll
/// </summary>
/// <param name="newRoll">The roll 'collection' we wish to add</param>
/// <returns>returns error message if validations fails, null if it's successfully added</returns>
public bool AddRoll(SettingsRoll newRoll)
{
if (!ValidateRoll(newRoll)) return false;
Rolls.Add(newRoll);
return true;
// todo add that specific roll message
}
public void RemoveRoll(SettingsRoll roll)
{
Rolls.Remove(roll);
// todo remove that specific roll macromessage
}
/// <summary>
/// Validates given roll
/// </summary>
/// <param name="newRoll">The roll we wish to validate</param>
/// <returns>Returns </returns>
private bool ValidateRoll(SettingsRoll newRoll)
{
var inputValid = newRoll.IsValid;
// existing values must not exist anywhere else
var mpExists = Rolls.Exists(p => p != newRoll && p.Roll.Value == newRoll.Roll.Value);
var rollExists = Rolls.Exists(p => p != newRoll && p.Roll.Value == newRoll.Roll.Value);
var colourExists = false;
if (newRoll.Colour.Value.HasValue)
{
colourExists = Rolls.Exists(p => p != newRoll && p.Colour.Value == newRoll.Colour.Value);
}
var fullNotExists = !mpExists && !rollExists && !colourExists;
return inputValid && fullNotExists;
}
/// <summary>
/// Does quick iteration to see if rolls are valid
/// </summary>
/// <returns>true if all rolls are valid</returns>
public bool ValidateAll()
{
foreach (var roll in Rolls)
{
if (!roll.IsValid) return false;
}
return true;
}
}

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
using HighRollerClassic.DataStructures;
namespace HighRollerClassic.SettingsStructure;
public class Settings(Configuration? configuration)
{
/// <summary>
/// Whether developer options should be visible in the settings
/// </summary>
public bool devOptions = false;
/// <summary>
/// Maximum bet that will be allowed to set
/// </summary>
public TrackedValue MaxBet { get; set; } = new(SettingValueType.MaxBet, null);
/// <summary>
/// How much the bet will change when user adjusts their bet via +/- keys
/// </summary>
public uint? Step { get; set; }
/// <summary>
/// Contains messages such as announcements etc.
/// </summary>
public List<MessageMacro> Macros { get; private set; } = new();
// todo we'll make this a new class because we have 3 roll methods already 'polluting' this class
public readonly RollsCollection RollsCollection = new();
// todo might get fucky wucky if maxbet is not yet defined
public bool IsValid => MaxBet.IsValid && StepValid() && RollsCollection.ValidateAll();
public bool Set => IsValid && RollsCollection.Size > 0 && Macros.Count > 0;
private bool StepValid()
{
uint maxBet;
if (configuration is { Settings.MaxBet.IsValid: true })
{
maxBet = configuration.Settings.MaxBet.Value!.Value; //IsValid guarantees validity
}
else if (MaxBet.IsValid)
{
maxBet = MaxBet.Value!.Value; // IsValid
}
else
{
maxBet = 0;
}
return Step <= maxBet;
}
}

View File

@@ -0,0 +1,35 @@
using System.Numerics;
using Dalamud.Interface;
using HighRollerClassic.DataStructures;
namespace HighRollerClassic.SettingsStructure;
/// <summary>
/// Stores individual 'Roll' setting (multiplier, roll, colour)
/// </summary>
/// <param name="multiplier">Roll multiplier</param>
/// <param name="roll">Actual roll value</param>
/// <param name="colour">Actual colour (can be null)</param>
public class SettingsRoll(uint? multiplier, uint? roll, Vector4? colour)
{
/// <summary>
/// Roll multiplier
/// </summary>
public TrackedValue Multiplier { get; set; } = new(SettingValueType.Multiplier, multiplier);
/// <summary>
/// Roll itself
/// </summary>
public TrackedValue Roll { get; set; } = new(SettingValueType.Roll, roll);
/// <summary>
/// Colour that will be used to highlight roll in history
/// </summary>
public TrackedValue Colour { get; set; } =
new(SettingValueType.Colour, colour.HasValue ? ColorHelpers.RgbaVector4ToUint(colour.Value) : null);
/// <summary>
/// Returns true if all properties in record are valid
/// </summary>
public bool IsValid => Multiplier.IsValid && Roll.IsValid && Colour.IsValid;
}

View File

@@ -0,0 +1,66 @@
using System;
using HighRollerClassic.DataStructures;
namespace HighRollerClassic.SettingsStructure;
/// <summary>
/// Tracks value and its validity
/// </summary>
/// <param name="type">Type of the value</param>
/// <param name="value">Its value</param>
public class TrackedValue(SettingValueType type, uint? value)
{
/// <summary>
/// The main value
/// </summary>
public uint? Value
{
get;
set
{
IsValid = CheckValid(value);
field = value;
}
} = value;
/// <summary>
/// Tracks validity of the value
/// </summary>
public bool IsValid { get; private set; }
/// <summary>
/// Minimum rng roll (/random)
/// </summary>
private const uint MinRoll = 1;
/// <summary>
/// Maximum rng roll (/random)
/// </summary>
private const uint MaxRoll = 999;
/// <summary>
/// Maximum possible gil in-game
/// </summary>
private const uint MaxGil = 999_999_999;
private readonly Func<uint?, bool> multiplierValid = v => v is not null;
private readonly Func<uint?, bool> rollValid = v => v is >= MinRoll and <= MaxRoll;
private readonly Func<uint?, bool> maxBetValid = v => v is <= MaxGil;
/// <summary>
/// Checks if the value is valid
/// </summary>
/// <param name="value"></param>
private bool CheckValid(uint? value)
{
return type switch
{
SettingValueType.Multiplier => multiplierValid(value),
SettingValueType.Roll => rollValid(value),
SettingValueType.Colour => true,
SettingValueType.MaxBet => maxBetValid(value),
// step will get checked upstream to prevent passing configuration to every tracked value
_ => false,
};
}
}

View File

@@ -32,9 +32,6 @@ public class GambaWindow : Window, IDisposable
name = target.TargetName;
}
private bool SettingsSet => configuration.Settings is
{ MaxBet: not null, Step: not null, Rolls.Count: > 0, Macros.Count: > 0 };
public void Dispose() { }
public override void Draw()
@@ -45,7 +42,7 @@ public class GambaWindow : Window, IDisposable
return;
}
if (!SettingsSet)
if (!configuration.Settings.Set)
{
ImGui.Text("Please set up settings");
return;

View File

@@ -4,6 +4,7 @@ using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Components;
using Dalamud.Interface.Windowing;
using HighRollerClassic.DataStructures;
using HighRollerClassic.SettingsStructure;
namespace HighRollerClassic.Windows;
@@ -13,20 +14,20 @@ public class SettingsWindow : Window, IDisposable
private const int Spacing = 5;
private const int InputWidth = 105;
private const int InputMaxLen = 11;
private const uint RollInputWidth = 50;
private const uint RollSettingInputWidth = 50;
private const uint MaxAllowedGil = 999_999_999;
private readonly Configuration configuration;
private readonly Vector4 red = new(1, 0, 0, 1f);
// colours
private readonly Vector4 red = new(1, 0, 0, 1f);
private readonly Vector4 yellow = new(249 / 255f, 180 / 255f, 45 / 255f, 1);
private SettingsRoll? newRoll;
private SettingsStructure.Settings settings;
private Settings settings;
private bool stepFormatValid = true;
/// <summary>
/// Tracks new roll
/// </summary>
private SettingsRoll? newRoll;
public SettingsWindow(Plugin plugin) : base("Settings##HRC")
{
@@ -36,7 +37,7 @@ public class SettingsWindow : Window, IDisposable
SizeCondition = ImGuiCond.Always;
configuration = plugin.Configuration;
settings = new Settings();
settings = new SettingsStructure.Settings(configuration);
}
public void Dispose() { }
@@ -45,46 +46,30 @@ public class SettingsWindow : Window, IDisposable
public override void Draw()
{
// todo integrate for every field
var drawList = ImGui.GetWindowDrawList();
var center = ImGui.GetCursorScreenPos();
const uint radius = 10;
var circleColor = ImGui.GetColorU32(yellow);
var borderColor = ImGui.GetColorU32(red);
var textColor = ImGui.GetColorU32(red);
drawList.AddCircleFilled(center + new Vector2(radius, radius), radius, circleColor);
drawList.AddCircle(center + new Vector2(radius, radius), radius, borderColor, 0, 2.5f);
const string text = "!";
var textSize = ImGui.CalcTextSize(text);
var textPos = center + new Vector2(
radius - (textSize.X * 0.5f),
radius - (textSize.Y * 0.5f)
);
drawList.AddText(textPos, textColor, text);
ImGui.Dummy(new Vector2(radius * 2f, radius * 2f));
// todo set up multiplier, roll, color, etc
// todo add button for rolls
if (ImGui.CollapsingHeader("Rolls##settings"))
{
if (ImGui.Button("Add roll") && TempRoll)
ImGui.BeginDisabled(newRoll != null);
if (ImGui.Button("Add roll") && newRoll == null)
{
// TODO no new rolls must be there
newRoll = new SettingsRoll();
newRoll = new SettingsRoll(null, null, null);
// todo display shit
// todo make it display actual error
ImGui.SameLine();
if (!newRoll.IsValid) ImGui.Text("ERROR");
}
ImGui.EndDisabled();
// todo display new roll
foreach (var roll in settings.RollsCollection.Rolls)
{
// todo here we put existing rolls
}
// todo here we put new rolls
// foreach (var roll in settings.rolls)
// {
// // todo here we put existing rolls
// }
}
if (ImGui.CollapsingHeader("General##settings"))
@@ -128,6 +113,33 @@ public class SettingsWindow : Window, IDisposable
if (ImGui.Button("Reset")) settings = configuration.Settings;
}
private void DrawError()
{
// todo integrate for every field
var drawList = ImGui.GetWindowDrawList();
var center = ImGui.GetCursorScreenPos();
const uint radius = 10;
var circleColor = ImGui.GetColorU32(yellow);
var borderColor = ImGui.GetColorU32(red);
var textColor = ImGui.GetColorU32(red);
drawList.AddCircleFilled(center + new Vector2(radius, radius), radius, circleColor);
drawList.AddCircle(center + new Vector2(radius, radius), radius, borderColor, 0, 2.5f);
const string text = "!";
var textSize = ImGui.CalcTextSize(text);
var textPos = center + new Vector2(
radius - (textSize.X * 0.5f),
radius - (textSize.Y * 0.5f)
);
drawList.AddText(textPos, textColor, text);
ImGui.Dummy(new Vector2(radius * 2f, radius * 2f));
}
/// <summary>
/// Returns false if the roll is to be removed
/// </summary>
@@ -138,7 +150,7 @@ public class SettingsWindow : Window, IDisposable
private (bool? valid, bool change) DisplayRollSetting(
ref uint multiplier, ref uint roll, ref Vector4 colour, ref uint id, ref)
{
ImGui.SetNextItemWidth(RollSettingInputWidth);
ImGui.SetNextItemWidth(RollInputWidth);
ImGui.InputUInt($"##multiplier{id}", ref multiplier);
ImGui.SameLine();
@@ -146,7 +158,7 @@ public class SettingsWindow : Window, IDisposable
ImGui.Text("x");
ImGui.SameLine();
ImGui.SetNextItemWidth(RollSettingInputWidth);
ImGui.SetNextItemWidth(RollInputWidth);
ImGui.InputUInt($"##roll{id}", ref roll);
ImGui.SameLine();
@@ -167,18 +179,6 @@ public class SettingsWindow : Window, IDisposable
var change =
}
private bool ValidateRollSetting()
{
// multiplier must not already exist
// roll must be between 1 and 999
// roll must not already exist
// colour must not exist already
return true;
}
/// <summary>
/// Returns true if valid, false if invalid or null if validity has no changed