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] [Serializable]
public class Configuration : IPluginConfiguration public class Configuration : IPluginConfiguration
{ {
public PlayerManager Players { get; set; }= new(); public PlayerManager Players { get; set; } = new();
public Settings Settings { get; set; } = new(); public SettingsStructure.Settings Settings { get; set; } = new(null);
public int Version { get; set; } = 0; 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; 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 void Dispose() { }
public override void Draw() public override void Draw()
@@ -45,7 +42,7 @@ public class GambaWindow : Window, IDisposable
return; return;
} }
if (!SettingsSet) if (!configuration.Settings.Set)
{ {
ImGui.Text("Please set up settings"); ImGui.Text("Please set up settings");
return; return;

View File

@@ -4,6 +4,7 @@ using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Components; using Dalamud.Interface.Components;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using HighRollerClassic.DataStructures; using HighRollerClassic.DataStructures;
using HighRollerClassic.SettingsStructure;
namespace HighRollerClassic.Windows; namespace HighRollerClassic.Windows;
@@ -13,20 +14,20 @@ public class SettingsWindow : Window, IDisposable
private const int Spacing = 5; private const int Spacing = 5;
private const int InputWidth = 105; private const int InputWidth = 105;
private const int InputMaxLen = 11; 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 Configuration configuration;
private readonly Vector4 red = new(1, 0, 0, 1f);
// colours // colours
private readonly Vector4 red = new(1, 0, 0, 1f);
private readonly Vector4 yellow = new(249 / 255f, 180 / 255f, 45 / 255f, 1); private readonly Vector4 yellow = new(249 / 255f, 180 / 255f, 45 / 255f, 1);
private SettingsRoll? newRoll; private SettingsStructure.Settings settings;
private Settings settings; /// <summary>
private bool stepFormatValid = true; /// Tracks new roll
/// </summary>
private SettingsRoll? newRoll;
public SettingsWindow(Plugin plugin) : base("Settings##HRC") public SettingsWindow(Plugin plugin) : base("Settings##HRC")
{ {
@@ -36,7 +37,7 @@ public class SettingsWindow : Window, IDisposable
SizeCondition = ImGuiCond.Always; SizeCondition = ImGuiCond.Always;
configuration = plugin.Configuration; configuration = plugin.Configuration;
settings = new Settings(); settings = new SettingsStructure.Settings(configuration);
} }
public void Dispose() { } public void Dispose() { }
@@ -45,46 +46,30 @@ public class SettingsWindow : Window, IDisposable
public override void Draw() 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 set up multiplier, roll, color, etc
// todo add button for rolls // todo add button for rolls
if (ImGui.CollapsingHeader("Rolls##settings")) 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(null, null, null);
newRoll = new SettingsRoll(); // 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")) if (ImGui.CollapsingHeader("General##settings"))
@@ -128,6 +113,33 @@ public class SettingsWindow : Window, IDisposable
if (ImGui.Button("Reset")) settings = configuration.Settings; 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> /// <summary>
/// Returns false if the roll is to be removed /// Returns false if the roll is to be removed
/// </summary> /// </summary>
@@ -138,7 +150,7 @@ public class SettingsWindow : Window, IDisposable
private (bool? valid, bool change) DisplayRollSetting( private (bool? valid, bool change) DisplayRollSetting(
ref uint multiplier, ref uint roll, ref Vector4 colour, ref uint id, ref) 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.InputUInt($"##multiplier{id}", ref multiplier);
ImGui.SameLine(); ImGui.SameLine();
@@ -146,7 +158,7 @@ public class SettingsWindow : Window, IDisposable
ImGui.Text("x"); ImGui.Text("x");
ImGui.SameLine(); ImGui.SameLine();
ImGui.SetNextItemWidth(RollSettingInputWidth); ImGui.SetNextItemWidth(RollInputWidth);
ImGui.InputUInt($"##roll{id}", ref roll); ImGui.InputUInt($"##roll{id}", ref roll);
ImGui.SameLine(); ImGui.SameLine();
@@ -167,18 +179,6 @@ public class SettingsWindow : Window, IDisposable
var change = 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> /// <summary>
/// Returns true if valid, false if invalid or null if validity has no changed /// Returns true if valid, false if invalid or null if validity has no changed