From 652a2a38da9374c91421b69cc1421721bed4fd0a Mon Sep 17 00:00:00 2001 From: Lichie Date: Tue, 14 Dec 2021 06:55:18 -0800 Subject: [PATCH 1/5] stupidly large change --- LMeter/ACT/ACTClient.cs | 72 ++++++++++++------ LMeter/ACT/ACTEvent.cs | 117 +++++++++++++++++------------- LMeter/ACT/LazyFloat.cs | 66 +++++++++++++++++ LMeter/ACT/LazyFloatConverter.cs | 44 +++++++++++ LMeter/ACT/TextTagFormatter.cs | 55 ++++++++++++++ LMeter/Config/ACTConfig.cs | 75 ++++++++++++++++--- LMeter/Config/AboutPage.cs | 4 +- LMeter/Config/BarConfig.cs | 17 +++-- LMeter/Config/HeaderConfig.cs | 74 ++++++++++--------- LMeter/Config/MeterListConfig.cs | 31 ++++++-- LMeter/Config/VisibilityConfig.cs | 8 ++ LMeter/Meter/MeterWindow.cs | 106 +++++++++++---------------- LMeter/PluginManager.cs | 36 ++++----- LMeter/changelog.md | 8 ++ 14 files changed, 498 insertions(+), 215 deletions(-) create mode 100644 LMeter/ACT/LazyFloat.cs create mode 100644 LMeter/ACT/LazyFloatConverter.cs create mode 100644 LMeter/ACT/TextTagFormatter.cs diff --git a/LMeter/ACT/ACTClient.cs b/LMeter/ACT/ACTClient.cs index 0ef2e41..29fa678 100644 --- a/LMeter/ACT/ACTClient.cs +++ b/LMeter/ACT/ACTClient.cs @@ -31,19 +31,21 @@ public class ACTClient : ILMeterDisposable private ACTEvent? LastEvent { get; set; } - public ConnectionStatus Status { get; private set; } + private ConnectionStatus _status { get; set; } public ACTClient() { this.Socket = new ClientWebSocket(); this.CancellationTokenSource = new CancellationTokenSource(); - this.Status = ConnectionStatus.NotConnected; + this._status = ConnectionStatus.NotConnected; } + public static ConnectionStatus Status => Singletons.Get()._status; + public static ACTEvent? GetLastEvent() { ACTClient client = Singletons.Get(); - if (client.Status != ConnectionStatus.Connected || + if (client._status != ConnectionStatus.Connected || client.LastEvent is null) { return null; @@ -64,22 +66,32 @@ public static void EndEncounter() chat.PrintChat(message); } - public static void ClearAct() + public static void Clear(bool clearAct) { - ChatGui chat = Singletons.Get(); - XivChatEntry message = new XivChatEntry() + Singletons.Get().LastEvent = null; + if (clearAct) { - Message = "clear", - Type = XivChatType.Echo - }; + ChatGui chat = Singletons.Get(); + XivChatEntry message = new XivChatEntry() + { + Message = "clear", + Type = XivChatType.Echo + }; - chat.PrintChat(message); - Singletons.Get().LastEvent = null; + chat.PrintChat(message); + } + } + + public static void RetryConnection(string address) + { + ACTClient client = Singletons.Get(); + client.Reset(); + client.Start(address); } public void Start(string host) { - if (this.Status != ConnectionStatus.NotConnected) + if (this._status != ConnectionStatus.NotConnected) { PluginLog.Error("Cannot start, ACTClient needs to be reset!"); return; @@ -91,8 +103,8 @@ public void Start(string host) } catch (Exception ex) { - this.Status = ConnectionStatus.ConnectionFailed; - PluginLog.Error($"{ex.ToString()}"); + this._status = ConnectionStatus.ConnectionFailed; + this.LogConnectionFailure(ex.ToString()); } } @@ -100,7 +112,7 @@ private async Task Connect(string host) { try { - this.Status = ConnectionStatus.Connecting; + this._status = ConnectionStatus.Connecting; await this.Socket.ConnectAsync(new Uri(host), this.CancellationTokenSource.Token); string subscribe = "{\"call\":\"subscribe\",\"events\":[\"CombatData\"]}"; @@ -112,20 +124,20 @@ await this.Socket.SendAsync( } catch (Exception ex) { - this.Status = ConnectionStatus.ConnectionFailed; - PluginLog.Error($"Failed to connect to ACT!\n{ex.ToString()}"); + this._status = ConnectionStatus.ConnectionFailed; + this.LogConnectionFailure(ex.ToString()); return; } ArraySegment buffer = new ArraySegment(new byte[4096]); if (buffer.Array is null) { - this.Status = ConnectionStatus.ConnectionFailed; - PluginLog.Error($"Failed to connect to ACT!\nFailed to allocate receive buffer!"); + this._status = ConnectionStatus.ConnectionFailed; + this.LogConnectionFailure("Failed to allocate receive buffer!"); return; } - this.Status = ConnectionStatus.Connected; + this._status = ConnectionStatus.Connected; PluginLog.Information("Successfully Established ACT Connection"); try { @@ -165,23 +177,28 @@ await this.Socket.SendAsync( } catch (Exception ex) { - PluginLog.Error(ex.ToString()); + this.LogConnectionFailure(ex.ToString()); } } } } } - while (this.Status == ConnectionStatus.Connected); + while (this._status == ConnectionStatus.Connected); } catch { // Swallow exception in case something weird happens during shutdown } + finally + { + this.Shutdown(); + } } public void Shutdown() { - this.Status = ConnectionStatus.ShuttingDown; + this._status = ConnectionStatus.ShuttingDown; + this.LastEvent = null; if (this.Socket.State == WebSocketState.Open || this.Socket.State == WebSocketState.Connecting) { @@ -207,6 +224,7 @@ public void Shutdown() } this.Socket.Dispose(); + this._status = ConnectionStatus.NotConnected; } public void Reset() @@ -214,7 +232,13 @@ public void Reset() this.Shutdown(); this.Socket = new ClientWebSocket(); this.CancellationTokenSource = new CancellationTokenSource(); - this.Status = ConnectionStatus.NotConnected; + this._status = ConnectionStatus.NotConnected; + } + + private void LogConnectionFailure(string error) + { + PluginLog.Debug($"Failed to connect to ACT!"); + PluginLog.Verbose(error); } public void Dispose() diff --git a/LMeter/ACT/ACTEvent.cs b/LMeter/ACT/ACTEvent.cs index 352aa01..1cc1403 100644 --- a/LMeter/ACT/ACTEvent.cs +++ b/LMeter/ACT/ACTEvent.cs @@ -4,6 +4,7 @@ using Newtonsoft.Json; using System; using LMeter.Helpers; +using System.Text.RegularExpressions; namespace LMeter.ACT { @@ -38,23 +39,23 @@ public static ACTEvent GetTestData() public class Encounter { + [JsonIgnore] + private static readonly Random _rand = new Random(); + + [JsonIgnore] + private static readonly Regex _regex = new Regex(@"\[(\w*)(-k)?\.?(\d+)?\]", RegexOptions.Compiled); + + [JsonIgnore] + private static readonly Dictionary _properties = typeof(Encounter).GetProperties().ToDictionary((x) => x.Name.ToLower()); + public static string[] GetTags() { return typeof(Encounter).GetProperties().Select(x => $"[{x.Name.ToLower()}]").ToArray(); } - public string GetFormattedString(string format) + public string GetFormattedString(string format, string numberFormat) { - foreach (PropertyInfo prop in this.GetType().GetProperties()) - { - string? value = prop.GetValue(this)?.ToString(); - if (value is not null) - { - format = format.Replace($"[{prop.Name.ToLower()}]", value); - } - } - - return format; + return _regex.Replace(format, new TextTagFormatter(this, numberFormat, _properties).Evaluate); } [JsonProperty("title")] @@ -67,37 +68,45 @@ public string GetFormattedString(string format) private string _duration { get; set; } = string.Empty; [JsonProperty("encdps")] - public string Dps { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? Dps { get; private set; } [JsonProperty("damage")] - public string DamageTotal { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? DamageTotal { get; private set; } [JsonProperty("enchps")] - public string Hps { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? Hps { get; private set; } [JsonProperty("healed")] - public string HealingTotal { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? HealingTotal { get; private set; } [JsonProperty("damagetaken")] - public string DamageTaken { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? DamageTaken { get; private set; } [JsonProperty("deaths")] - public string Deaths { get; private set; } = string.Empty; + public string? Deaths { get; private set; } [JsonProperty("kills")] - public string Kills { get; private set; } = string.Empty; + public string? Kills { get; private set; } public static Encounter GetTestData() { + float damage = _rand.Next(212345 * 8); + float healing = _rand.Next(41234 * 8); + return new Encounter() { - Duration = "00:15", + Duration = "00:30", Title = "Preview", - Dps = "69420", - Hps = "42069", + Dps = new LazyFloat(damage / 30), + Hps = new LazyFloat(healing / 30), Deaths = "0", - DamageTotal = "69420", - HealingTotal = "42069" + DamageTotal = new LazyFloat(damage), + HealingTotal = new LazyFloat(healing) }; } } @@ -105,25 +114,22 @@ public static Encounter GetTestData() public class Combatant { [JsonIgnore] - private static Random _rand = new Random(); + private static readonly Random _rand = new Random(); + + [JsonIgnore] + private static readonly Regex _regex = new Regex(@"\[(\w*)(-k)?\.?(\d+)?\]", RegexOptions.Compiled); + + [JsonIgnore] + private static readonly Dictionary _properties = typeof(Combatant).GetProperties().ToDictionary((x) => x.Name.ToLower()); public static string[] GetTags() { return typeof(Combatant).GetProperties().Select(x => $"[{x.Name.ToLower()}]").ToArray(); } - public string GetFormattedString(string format) + public string GetFormattedString(string format, string numberFormat) { - foreach (PropertyInfo prop in this.GetType().GetProperties()) - { - string? value = prop.GetValue(this)?.ToString(); - if (value is not null) - { - format = format.Replace($"[{prop.Name.ToLower()}]", value); - } - } - - return format; + return _regex.Replace(format, new TextTagFormatter(this, numberFormat, _properties).Evaluate); } [JsonProperty("name")] @@ -136,13 +142,16 @@ public string GetFormattedString(string format) public string Duration { get; private set; } = string.Empty; [JsonProperty("encdps")] - public string EncDps { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? EncDps { get; private set; } [JsonProperty("dps")] - public string Dps { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? Dps { get; private set; } [JsonProperty("damage")] - public string DamageTotal { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? DamageTotal { get; private set; } [JsonProperty("damage%")] public string DamagePct { get; private set; } = string.Empty; @@ -157,19 +166,23 @@ public string GetFormattedString(string format) public string CritDirectHitPct { get; private set; } = string.Empty; [JsonProperty("enchps")] - public string EncHps { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? EncHps { get; private set; } [JsonProperty("hps")] - public string Hps { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? Hps { get; private set; } [JsonProperty("healed")] - public string HealingTotal { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? HealingTotal { get; private set; } [JsonProperty("healed%")] public string HealingPct { get; private set; } = string.Empty; [JsonProperty("damagetaken")] - public string DamageTaken { get; private set; } = string.Empty; + [JsonConverter(typeof(LazyFloatConverter))] + public LazyFloat? DamageTaken { get; private set; } [JsonProperty("deaths")] public string Deaths { get; private set; } = string.Empty; @@ -202,26 +215,26 @@ public static Dictionary GetTestData() private static Combatant GetCombatant(params string[] jobs) { - int damage = _rand.Next(200000); - int healing = _rand.Next(50000); + int damage = _rand.Next(212345); + int healing = _rand.Next(41234); return new Combatant() { Name = "Fake Name", - Duration = "00:15", + Duration = "00:30", Job = jobs.Select(x => x.ToString()).ElementAt(_rand.Next(jobs.Length)), - DamageTotal = damage.ToString(), - Dps = (damage / 15).ToString(), - EncDps = (damage / 15).ToString(), - HealingTotal = healing.ToString(), - Hps = (healing / 15).ToString(), - EncHps = (healing / 15).ToString(), + DamageTotal = new LazyFloat(damage.ToString()), + Dps = new LazyFloat((damage / 30).ToString()), + EncDps = new LazyFloat((damage / 30).ToString()), + HealingTotal = new LazyFloat(healing.ToString()), + Hps = new LazyFloat((healing / 30).ToString()), + EncHps = new LazyFloat((healing / 30).ToString()), DamagePct = "100%", HealingPct = "100%", CritHitPct = "20%", DirectHitPct = "25%", CritDirectHitPct = "5%", - DamageTaken = (damage / 20).ToString(), + DamageTaken = new LazyFloat((damage / 20).ToString()), Deaths = _rand.Next(2).ToString(), MaxHit = "Full Thrust-42069" }; diff --git a/LMeter/ACT/LazyFloat.cs b/LMeter/ACT/LazyFloat.cs new file mode 100644 index 0000000..82cf1b6 --- /dev/null +++ b/LMeter/ACT/LazyFloat.cs @@ -0,0 +1,66 @@ + +using System.Globalization; + +namespace LMeter.ACT +{ + public class LazyFloat + { + private float _value = 0; + + public string? Input { get; private set; } + + public bool WasGenerated { get; private set; } + + public float Value + { + get + { + if(this.WasGenerated) + { + return this._value; + } + + if (float.TryParse(this.Input, NumberStyles.Float, CultureInfo.InvariantCulture, out float parsed) && + !float.IsNaN(parsed)) + { + this._value = parsed; + } + else + { + this._value = 0; + } + + this.WasGenerated = true; + return this._value; + } + } + + public LazyFloat(string? input) + { + this.Input = input; + } + + public LazyFloat(float value) + { + this._value = value; + this.WasGenerated = true; + } + + public string? ToString(string format, bool kilo) + { + return kilo ? KiloFormat(this.Value, format) : this.Value.ToString(format, CultureInfo.InvariantCulture); + } + + public override string? ToString() + { + return this.Value.ToString(); + } + + private static string KiloFormat(float num, string format) => num switch + { + >= 1000000 => (num / 1000000f).ToString(format, CultureInfo.InvariantCulture) + "M", + >= 1000 => (num / 1000f).ToString(format, CultureInfo.InvariantCulture) + "K", + _ => num.ToString(format, CultureInfo.InvariantCulture) + }; + } +} \ No newline at end of file diff --git a/LMeter/ACT/LazyFloatConverter.cs b/LMeter/ACT/LazyFloatConverter.cs new file mode 100644 index 0000000..5a41c22 --- /dev/null +++ b/LMeter/ACT/LazyFloatConverter.cs @@ -0,0 +1,44 @@ + +using System; +using Newtonsoft.Json; + +namespace LMeter.ACT +{ + public class LazyFloatConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + throw new NotImplementedException("Write not supported."); + } + + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + { + if (objectType != typeof(LazyFloat)) + { + return serializer.Deserialize(reader, objectType); + } + + if (reader.TokenType != JsonToken.String) + { + return new LazyFloat(0f); + } + + return new LazyFloat(serializer.Deserialize(reader, typeof(string))?.ToString()); + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanWrite + { + get { return false; } + } + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(LazyFloat); + } + } +} \ No newline at end of file diff --git a/LMeter/ACT/TextTagFormatter.cs b/LMeter/ACT/TextTagFormatter.cs new file mode 100644 index 0000000..2a12260 --- /dev/null +++ b/LMeter/ACT/TextTagFormatter.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace LMeter.ACT +{ + public class TextTagFormatter + { + private string Format { get; set; } + private Dictionary Properties { get; set; } + private object Source { get; set; } + + public TextTagFormatter( + object source, + string format, + Dictionary properties) + { + this.Source = source; + this.Format = format; + this.Properties = properties; + } + + public string Evaluate(Match m) + { + if (m.Groups.Count != 4) + { + return m.Value; + } + + string format = string.IsNullOrEmpty(m.Groups[3].Value) + ? $"{this.Format}0" + : $"{this.Format}{m.Groups[3].Value}"; + + string? value = null; + string key = m.Groups[1].Value; + + if (this.Properties.ContainsKey(key)) + { + object? propValue = this.Properties[m.Groups[1].Value].GetValue(this.Source); + + if (propValue is LazyFloat lazyFloat) + { + bool kilo = !string.IsNullOrEmpty(m.Groups[2].Value); + value = lazyFloat.ToString(format, kilo); + } + else + { + value = propValue?.ToString(); + } + } + + return value ?? m.Value; + } + } +} \ No newline at end of file diff --git a/LMeter/Config/ACTConfig.cs b/LMeter/Config/ACTConfig.cs index 6d95430..6f2c55d 100644 --- a/LMeter/Config/ACTConfig.cs +++ b/LMeter/Config/ACTConfig.cs @@ -1,3 +1,4 @@ +using System; using System.Numerics; using Dalamud.Interface; using ImGuiNET; @@ -10,11 +11,22 @@ namespace LMeter.Config public class ACTConfig : IConfigPage { [JsonIgnore] - private string _defaultSocketAddress = "ws://127.0.0.1:10501/ws"; + private const string _defaultSocketAddress = "ws://127.0.0.1:10501/ws"; + + [JsonIgnore] + private DateTime? LastCombatTime { get; set; } = null; + + [JsonIgnore] + private DateTime? LastReconnectAttempt { get; set; } = null; public string Name => "ACT"; public string ACTSocketAddress; + + public bool AutoReconnect = false; + public int ReconnectDelay = 30; + + public bool ClearACT = false; public bool AutoEnd = false; public int AutoEndDelay = 3; @@ -28,20 +40,32 @@ public void DrawConfig(Vector2 size, float padX, float padY) if (ImGui.BeginChild($"##{this.Name}", new Vector2(size.X, size.Y), true)) { Vector2 buttonSize = new Vector2(40, 0); - ACTClient client = Singletons.Get(); - ImGui.Text($"ACT Status: {client.Status}"); + ImGui.Text($"ACT Status: {ACTClient.Status}"); ImGui.InputTextWithHint("ACT Websocket Address", $"Default: '{_defaultSocketAddress}'", ref this.ACTSocketAddress, 64); - DrawHelpers.DrawButton(string.Empty, FontAwesomeIcon.Sync, () => RetryACTConnection(), "Reconnect", buttonSize); + DrawHelpers.DrawButton(string.Empty, FontAwesomeIcon.Sync, () => ACTClient.RetryConnection(this.ACTSocketAddress), "Reconnect", buttonSize); ImGui.SameLine(); ImGui.SetCursorPosY(ImGui.GetCursorPosY() - 1f); ImGui.Text("Retry ACT Connection"); ImGui.NewLine(); + ImGui.Checkbox("Automatically attempt to reconnect if connection fails", ref this.AutoReconnect); + if (this.AutoReconnect) + { + DrawHelpers.DrawNestIndicator(1); + ImGui.PushItemWidth(30); + ImGui.InputInt("Seconds between reconnect attempts", ref this.ReconnectDelay, 0, 0); + ImGui.PopItemWidth(); + } + + + ImGui.NewLine(); + ImGui.Checkbox("Clear ACT when clearing LMeter", ref this.ClearACT); ImGui.Checkbox("Force ACT to end encounter after combat", ref this.AutoEnd); if (ImGui.IsItemHovered()) { - ImGui.SetTooltip("Only enable this if ACT's auto encounter ending isn't working"); + ImGui.SetTooltip("It is recommended to disable ACT Command Sounds if you use this feature.\n" + + "The option can be found in ACT under Options -> Sound Settings."); } if (this.AutoEnd) @@ -58,20 +82,49 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.SetCursorPosY(ImGui.GetCursorPosY() - 1f); ImGui.Text("Force End Combat"); - DrawHelpers.DrawButton(string.Empty, FontAwesomeIcon.Trash, () => ACTClient.ClearAct(), null, buttonSize); + DrawHelpers.DrawButton(string.Empty, FontAwesomeIcon.Trash, () => Singletons.Get().Clear(this.ClearACT), null, buttonSize); ImGui.SameLine(); ImGui.SetCursorPosY(ImGui.GetCursorPosY() - 1f); - ImGui.Text("Clear ACT Encounters"); + ImGui.Text("Clear LMeter"); } ImGui.EndChild(); } - public void RetryACTConnection() + public void TryReconnect() { - ACTClient client = Singletons.Get(); - client.Reset(); - client.Start(this.ACTSocketAddress); + if (ACTClient.Status == ConnectionStatus.NotConnected || + ACTClient.Status == ConnectionStatus.ConnectionFailed) + { + if (this.AutoReconnect && + this.LastReconnectAttempt < DateTime.UtcNow - TimeSpan.FromSeconds(this.ReconnectDelay)) + { + ACTClient.RetryConnection(this.ACTSocketAddress); + this.LastReconnectAttempt = DateTime.UtcNow; + } + } + else + { + this.LastReconnectAttempt = DateTime.UtcNow; + } + } + + public void TryEndEncounter() + { + if (ACTClient.Status == ConnectionStatus.Connected) + { + if (this.AutoEnd && + CharacterState.IsInCombat()) + { + this.LastCombatTime = DateTime.UtcNow; + } + else if (this.LastCombatTime is not null && + this.LastCombatTime < DateTime.UtcNow - TimeSpan.FromSeconds(this.AutoEndDelay)) + { + ACTClient.EndEncounter(); + this.LastCombatTime = null; + } + } } } } diff --git a/LMeter/Config/AboutPage.cs b/LMeter/Config/AboutPage.cs index 8a0c502..f223ccb 100644 --- a/LMeter/Config/AboutPage.cs +++ b/LMeter/Config/AboutPage.cs @@ -57,9 +57,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) } ImGui.SameLine(); - if (ImGui.Button("Donate", buttonSize)) + if (ImGui.Button("Discord", buttonSize)) { - Utils.OpenUrl("https://ko-fi.com/lichie"); + Utils.OpenUrl("https://discord.gg/delvui"); } ImGui.PopStyleVar(); diff --git a/LMeter/Config/BarConfig.cs b/LMeter/Config/BarConfig.cs index 154861b..3f6a309 100644 --- a/LMeter/Config/BarConfig.cs +++ b/LMeter/Config/BarConfig.cs @@ -14,10 +14,13 @@ public class BarConfig : IConfigPage private static string[] _jobIconStyleOptions = new string[] { "Style 1", "Style 2" }; public int BarCount = 8; + public int BarGaps = 0; public bool ShowJobIcon = true; public int JobIconStyle = 0; public Vector2 JobIconOffset = new Vector2(0, 0); + + public bool ThousandsSeparators = true; public bool UseJobColor = true; public ConfigColor BarColor = new ConfigColor(.3f, .3f, .3f, 1f); @@ -30,7 +33,7 @@ public class BarConfig : IConfigPage public int BarNameFontId = 0; public bool UseCharacterName = false; - public string BarDataFormat = "[damagetotal] ([encdps], [damagepct]) "; + public string BarDataFormat = "[damagetotal-k.1] ([encdps-k.1], [damagepct]) "; public ConfigColor BarDataColor = new ConfigColor(1, 1, 1, 1); public bool BarDataShowOutline = true; public ConfigColor BarDataOutlineColor = new ConfigColor(0, 0, 0, 0.5f); @@ -46,7 +49,7 @@ public Vector2 DrawBar( float top, float current) { - float barHeight = size.Y / this.BarCount; + float barHeight = (size.Y - (this.BarCount - 1) * this.BarGaps) / this.BarCount; Vector2 barSize = new Vector2(size.X, barHeight); Vector2 barFillSize = new Vector2(size.X * (current / top), barHeight); drawList.AddRectFilled(localPos, localPos + barFillSize, barColor.Base); @@ -59,7 +62,7 @@ public Vector2 DrawBar( } bool fontPushed = FontsManager.PushFont(this.BarNameFontKey); - string nameText = combatant.GetFormattedString(this.BarNameFormat); + string nameText = combatant.GetFormattedString(this.BarNameFormat, this.ThousandsSeparators ? "N" : "F"); if (this.UseCharacterName && combatant.Name.Contains("YOU")) { string characterName = Singletons.Get().LocalPlayer?.Name.ToString() ?? "YOU"; @@ -82,7 +85,7 @@ public Vector2 DrawBar( } fontPushed = FontsManager.PushFont(this.BarDataFontKey); - string dataText = combatant.GetFormattedString(this.BarDataFormat); + string dataText = combatant.GetFormattedString(this.BarDataFormat, this.ThousandsSeparators ? "N" : "F"); Vector2 dataTextSize = ImGui.CalcTextSize(dataText); Vector2 dataPos = Utils.GetAnchoredPosition(localPos, -barSize, DrawAnchor.Right); dataPos = Utils.GetAnchoredPosition(dataPos, dataTextSize, DrawAnchor.Right); @@ -98,7 +101,7 @@ public Vector2 DrawBar( ImGui.PopFont(); } - return localPos.AddY(barHeight); + return localPos.AddY(barHeight + this.BarGaps); } public void DrawConfig(Vector2 size, float padX, float padY) @@ -112,6 +115,8 @@ public void DrawConfig(Vector2 size, float padX, float padY) if (ImGui.BeginChild($"##{this.Name}", new Vector2(size.X, size.Y), true)) { ImGui.DragInt("Num Bars to Display", ref this.BarCount, 1, 1, 48); + ImGui.DragInt("Bar Gap Size", ref this.BarGaps, 1, 0, 20); + ImGui.Checkbox("Show Job Icon", ref this.ShowJobIcon); if (this.ShowJobIcon) { @@ -131,6 +136,8 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.ColorEdit4("Bar Color", ref vector, ImGuiColorEditFlags.AlphaPreview | ImGuiColorEditFlags.AlphaBar); this.BarColor.Vector = vector; } + + ImGui.Checkbox("Use Thousands Separators for Numbers", ref this.ThousandsSeparators); ImGui.NewLine(); ImGui.Checkbox("Use your name instead of 'YOU'", ref this.UseCharacterName); diff --git a/LMeter/Config/HeaderConfig.cs b/LMeter/Config/HeaderConfig.cs index 1bce8c1..2608e1f 100644 --- a/LMeter/Config/HeaderConfig.cs +++ b/LMeter/Config/HeaderConfig.cs @@ -24,8 +24,6 @@ public class HeaderConfig : IConfigPage public ConfigColor DurationColor = new ConfigColor(0f / 255f, 190f / 255f, 225f / 255f, 1f); public bool DurationShowOutline = true; public ConfigColor DurationOutlineColor = new ConfigColor(0, 0, 0, 0.5f); - public DrawAnchor DurationAlign = DrawAnchor.Left; - public Vector2 DurationOffset = new Vector2(0, 0); public int DurationFontId = 0; public string DurationFontKey = FontsManager.DalamudFontKey; @@ -33,8 +31,6 @@ public class HeaderConfig : IConfigPage public ConfigColor NameColor = new ConfigColor(1, 1, 1, 1); public bool NameShowOutline = true; public ConfigColor NameOutlineColor = new ConfigColor(0, 0, 0, 0.5f); - public DrawAnchor NameAlign = DrawAnchor.Left; - public Vector2 NameOffset = new Vector2(0, 0); public int NameFontId = 0; public string NameFontKey = FontsManager.DalamudFontKey; @@ -47,6 +43,7 @@ public class HeaderConfig : IConfigPage public int StatsFontId = 0; public string StatsFontKey = FontsManager.DalamudFontKey; public string StatsFormat = "[dps]rdps [hps]rhps Deaths: [deaths] "; + public bool ThousandsSeparators = true; public Vector2 DrawHeader(Vector2 pos, Vector2 size, Encounter? encounter, ImDrawListPtr drawList) { @@ -58,15 +55,15 @@ public Vector2 DrawHeader(Vector2 pos, Vector2 size, Encounter? encounter, ImDra Vector2 headerSize = new Vector2(size.X, this.HeaderHeight); drawList.AddRectFilled(pos, pos + headerSize, this.BackgroundColor.Base); - Vector2 durationPos = Vector2.Zero; + Vector2 durationPos = pos; Vector2 durationSize = Vector2.Zero; if (this.ShowEncounterDuration) { bool fontPushed = FontsManager.PushFont(this.DurationFontKey); - string duration = encounter is null ? $" LMeter v{Plugin.Version} " : $" {encounter.Duration} "; + string duration = encounter is null ? $" LMeter v{Plugin.Version}" : $" {encounter.Duration}"; durationSize = ImGui.CalcTextSize(duration); - durationPos = Utils.GetAnchoredPosition(pos + this.DurationOffset, -headerSize, DrawAnchor.Left); - durationPos = Utils.GetAnchoredPosition(durationPos, durationSize, this.DurationAlign); + durationPos = Utils.GetAnchoredPosition(durationPos, -headerSize, DrawAnchor.Left); + durationPos = Utils.GetAnchoredPosition(durationPos, durationSize, DrawAnchor.Left); DrawHelpers.DrawText(drawList, duration, durationPos, this.DurationColor.Base, this.DurationShowOutline, this.DurationOutlineColor.Base); if (fontPushed) { @@ -74,28 +71,17 @@ public Vector2 DrawHeader(Vector2 pos, Vector2 size, Encounter? encounter, ImDra } } - if (this.ShowEncounterName && encounter is not null) - { - bool fontPushed = FontsManager.PushFont(this.NameFontKey); - string name = encounter.Title; - Vector2 namePos = durationPos.AddX(durationSize.X) + this.NameOffset; - DrawHelpers.DrawText(drawList, name, namePos, this.NameColor.Base, this.NameShowOutline, this.NameOutlineColor.Base); - if (fontPushed) - { - ImGui.PopFont(); - } - } - + Vector2 raidStatsSize = Vector2.Zero; if (this.ShowRaidStats && encounter is not null) { - string text = encounter.GetFormattedString(this.StatsFormat); + string text = encounter.GetFormattedString(this.StatsFormat, this.ThousandsSeparators ? "N" : "F"); if (!string.IsNullOrEmpty(text)) { bool fontPushed = FontsManager.PushFont(this.StatsFontKey); - Vector2 statsSize = ImGui.CalcTextSize(text); + raidStatsSize = ImGui.CalcTextSize(text); Vector2 statsPos = Utils.GetAnchoredPosition(pos + this.StatsOffset, -headerSize, DrawAnchor.Right); - statsPos = Utils.GetAnchoredPosition(statsPos, statsSize, this.StatsAlign); + statsPos = Utils.GetAnchoredPosition(statsPos, raidStatsSize, this.StatsAlign); DrawHelpers.DrawText(drawList, text, statsPos, this.RaidStatsColor.Base, this.StatsShowOutline, this.StatsOutlineColor.Base); if (fontPushed) { @@ -103,6 +89,33 @@ public Vector2 DrawHeader(Vector2 pos, Vector2 size, Encounter? encounter, ImDra } } } + + if (this.ShowEncounterName && encounter is not null) + { + bool fontPushed = FontsManager.PushFont(this.NameFontKey); + string name = $" {encounter.Title}"; + Vector2 nameSize = ImGui.CalcTextSize(name); + + if (durationSize.X + raidStatsSize.X + nameSize.X > size.X) + { + float elipsesWidth = ImGui.CalcTextSize("...").X; + do + { + name = new string(name.AsSpan(0, name.Length - 1)); + nameSize = ImGui.CalcTextSize(name); + } + while (durationSize.X + raidStatsSize.X + nameSize.X + elipsesWidth > size.X); + name += "..."; + } + + Vector2 namePos = Utils.GetAnchoredPosition(new Vector2(durationPos.X + durationSize.X, pos.Y), -headerSize, DrawAnchor.Left); + namePos = Utils.GetAnchoredPosition(namePos, nameSize, DrawAnchor.Left); + DrawHelpers.DrawText(drawList, name, namePos, this.NameColor.Base, this.NameShowOutline, this.NameOutlineColor.Base); + if (fontPushed) + { + ImGui.PopFont(); + } + } return pos.AddY(this.HeaderHeight); } @@ -133,9 +146,6 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.Checkbox("Show Encounter Duration", ref this.ShowEncounterDuration); if (this.ShowEncounterDuration) { - DrawHelpers.DrawNestIndicator(2); - ImGui.DragFloat2("Position Offset##Duration", ref this.DurationOffset); - if (!FontsManager.ValidateFont(fontOptions, this.DurationFontId, this.DurationFontKey)) { this.DurationFontId = 0; @@ -146,9 +156,6 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.Combo("Font##Duration", ref this.DurationFontId, fontOptions, fontOptions.Length); this.DurationFontKey = fontOptions[this.DurationFontId]; - DrawHelpers.DrawNestIndicator(2); - ImGui.Combo("Text Align##Duration", ref Unsafe.As(ref this.DurationAlign), _anchorOptions, _anchorOptions.Length); - DrawHelpers.DrawNestIndicator(2); vector = this.DurationColor.Vector; ImGui.ColorEdit4("Text Color##Duration", ref vector, ImGuiColorEditFlags.AlphaPreview | ImGuiColorEditFlags.AlphaBar); @@ -170,9 +177,6 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.Checkbox("Show Encounter Name", ref this.ShowEncounterName); if (this.ShowEncounterName) { - DrawHelpers.DrawNestIndicator(2); - ImGui.DragFloat2("Position Offset##Name", ref this.NameOffset); - if (!FontsManager.ValidateFont(fontOptions, this.NameFontId, this.NameFontKey)) { this.NameFontId = 0; @@ -183,9 +187,6 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.Combo("Font##Name", ref this.NameFontId, fontOptions, fontOptions.Length); this.NameFontKey = fontOptions[this.NameFontId]; - DrawHelpers.DrawNestIndicator(2); - ImGui.Combo("Text Align##Name", ref Unsafe.As(ref this.NameAlign), _anchorOptions, _anchorOptions.Length); - DrawHelpers.DrawNestIndicator(2); vector = this.NameColor.Vector; ImGui.ColorEdit4("Text Color##Name", ref vector, ImGuiColorEditFlags.AlphaPreview | ImGuiColorEditFlags.AlphaBar); @@ -209,6 +210,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) { DrawHelpers.DrawNestIndicator(2); ImGui.InputText("Raid Stats Format", ref this.StatsFormat, 128); + + DrawHelpers.DrawNestIndicator(2); + ImGui.Checkbox("Use Thousands Separators for Numbers", ref this.ThousandsSeparators); if (ImGui.IsItemHovered()) { diff --git a/LMeter/Config/MeterListConfig.cs b/LMeter/Config/MeterListConfig.cs index 596559f..ab1547f 100644 --- a/LMeter/Config/MeterListConfig.cs +++ b/LMeter/Config/MeterListConfig.cs @@ -16,7 +16,7 @@ public class MeterListConfig : IConfigPage [JsonIgnore] private string _input = string.Empty; - public string Name => "Meters"; + public string Name => "Profiles"; public List Meters { get; init; } @@ -39,7 +39,7 @@ private void DrawCreateMenu(Vector2 size, float padX) if (ImGui.BeginChild("##Buttons", new Vector2(size.X, MenuBarHeight), true)) { ImGui.PushItemWidth(textInputWidth); - ImGui.InputTextWithHint("##Input", "Meter Name/Import String", ref _input, 10000); + ImGui.InputTextWithHint("##Input", "Profile Name/Import String", ref _input, 10000); ImGui.PopItemWidth(); ImGui.SameLine(); @@ -63,13 +63,14 @@ private void DrawMeterTable(Vector2 size, float padX) ImGuiTableFlags.ScrollY | ImGuiTableFlags.NoSavedSettings; - if (ImGui.BeginTable("##Meter_Table", 2, flags, new Vector2(size.X, size.Y - MenuBarHeight))) + if (ImGui.BeginTable("##Meter_Table", 3, flags, new Vector2(size.X, size.Y - MenuBarHeight))) { Vector2 buttonsize = new Vector2(30, 0); float actionsWidth = buttonsize.X * 3 + padX * 2; - ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch, 0, 0); - ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, actionsWidth, 1); + ImGui.TableSetupColumn(" #", ImGuiTableColumnFlags.WidthFixed, 18, 0); + ImGui.TableSetupColumn("Profile Name", ImGuiTableColumnFlags.WidthStretch, 0, 1); + ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, actionsWidth, 2); ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableHeadersRow(); @@ -88,12 +89,22 @@ private void DrawMeterTable(Vector2 size, float padX) ImGui.TableNextRow(ImGuiTableRowFlags.None, 28); if (ImGui.TableSetColumnIndex(0)) + { + string num = $" {i + 1}."; + float columnWidth = ImGui.GetColumnWidth(); + Vector2 cursorPos = ImGui.GetCursorPos(); + Vector2 textSize = ImGui.CalcTextSize(num); + ImGui.SetCursorPos(new Vector2(cursorPos.X + columnWidth - textSize.X, cursorPos.Y + 3f)); + ImGui.Text(num); + } + + if (ImGui.TableSetColumnIndex(1)) { ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 3f); ImGui.Text(meter.Name); } - if (ImGui.TableSetColumnIndex(1)) + if (ImGui.TableSetColumnIndex(2)) { ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 1f); DrawHelpers.DrawButton(string.Empty, FontAwesomeIcon.Pen, () => EditMeter(meter), "Edit", buttonsize); @@ -119,6 +130,14 @@ private void CreateMeter(string name) this._input = string.Empty; } + + public void ToggleMeter(int meterIndex) + { + if (meterIndex >= 0 && meterIndex < this.Meters.Count) + { + this.Meters[meterIndex].VisibilityConfig.AlwaysHide ^= true; + } + } private void EditMeter(MeterWindow meter) { diff --git a/LMeter/Config/VisibilityConfig.cs b/LMeter/Config/VisibilityConfig.cs index 3cb276f..6ba7eb1 100644 --- a/LMeter/Config/VisibilityConfig.cs +++ b/LMeter/Config/VisibilityConfig.cs @@ -6,6 +6,7 @@ using LMeter.Helpers; using System.Linq; using System.Collections.Generic; +using LMeter.ACT; namespace LMeter.Config { @@ -23,6 +24,7 @@ public class VisibilityConfig : IConfigPage public bool HideOutsideDuty = false; public bool HideWhilePerforming = false; public bool HideInGoldenSaucer = false; + public bool HideIfNotConnected = false; public JobType ShowForJobTypes = JobType.All; public string CustomJobString = string.Empty; @@ -60,6 +62,11 @@ public bool IsVisible() return false; } + if (this.HideIfNotConnected && ACTClient.Status != ConnectionStatus.Connected) + { + return false; + } + if (this.ShowForJobTypes == JobType.All) { return true; @@ -83,6 +90,7 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.Checkbox("Hide Outside Duty", ref this.HideOutsideDuty); ImGui.Checkbox("Hide While Performing", ref this.HideWhilePerforming); ImGui.Checkbox("Hide In Golden Saucer", ref this.HideInGoldenSaucer); + ImGui.Checkbox("Hide While Not Connected to ACT", ref this.HideIfNotConnected); DrawHelpers.DrawSpacing(1); string[] jobTypeOptions = Enum.GetNames(typeof(JobType)); diff --git a/LMeter/Meter/MeterWindow.cs b/LMeter/Meter/MeterWindow.cs index 38baacd..2b77834 100644 --- a/LMeter/Meter/MeterWindow.cs +++ b/LMeter/Meter/MeterWindow.cs @@ -188,60 +188,52 @@ private void DrawBars(ImDrawListPtr drawList, Vector2 localPos, Vector2 size, AC { List sortedCombatants = this.GetSortedCombatants(actEvent, this.GeneralConfig.DataType); - string topDataSource = this.GeneralConfig.DataType switch + float top = this.GeneralConfig.DataType switch { - MeterDataType.Damage => sortedCombatants[0].DamageTotal, - MeterDataType.Healing => sortedCombatants[0].HealingTotal, - MeterDataType.DamageTaken => sortedCombatants[0].DamageTaken, - _ => sortedCombatants[0].DamageTotal + MeterDataType.Damage => sortedCombatants[0].DamageTotal?.Value ?? 0, + MeterDataType.Healing => sortedCombatants[0].HealingTotal?.Value ?? 0, + MeterDataType.DamageTaken => sortedCombatants[0].DamageTaken?.Value ?? 0, + _ => 0 }; - if (float.TryParse(topDataSource, NumberStyles.Float, CultureInfo.InvariantCulture, out float top) && !float.IsNaN(top)) + int i = 0; + if (sortedCombatants.Count > this.BarConfig.BarCount) { - int i = 0; - if (sortedCombatants.Count > this.BarConfig.BarCount) - { - i = Math.Clamp(this.ScrollPosition, 0, sortedCombatants.Count - this.BarConfig.BarCount); - this.ScrollPosition = i; - } - - int max = Math.Min(i + this.BarConfig.BarCount, sortedCombatants.Count); - for (; i < max; i++) - { - Combatant combatant = sortedCombatants[i]; - - string currentDataSource = this.GeneralConfig.DataType switch - { - MeterDataType.Damage => combatant.DamageTotal, - MeterDataType.Healing => combatant.HealingTotal, - MeterDataType.DamageTaken => combatant.DamageTaken, - _ => combatant.DamageTotal - }; + i = Math.Clamp(this.ScrollPosition, 0, sortedCombatants.Count - this.BarConfig.BarCount); + this.ScrollPosition = i; + } - if (!float.TryParse(currentDataSource, NumberStyles.Float, CultureInfo.InvariantCulture, out float current) || float.IsNaN(current)) - { - return; - } + int maxIndex = Math.Min(i + this.BarConfig.BarCount, sortedCombatants.Count); + for (; i < maxIndex; i++) + { + Combatant combatant = sortedCombatants[i]; - ConfigColor barColor; - if (this.BarConfig.UseJobColor) + float current = this.GeneralConfig.DataType switch + { + MeterDataType.Damage => combatant.DamageTotal?.Value ?? 0, + MeterDataType.Healing => combatant.HealingTotal?.Value ?? 0, + MeterDataType.DamageTaken => combatant.DamageTaken?.Value ?? 0, + _ => 0 + }; + + ConfigColor barColor; + if (this.BarConfig.UseJobColor) + { + if (Enum.TryParse(combatant.Job, true, out Job job)) { - if (Enum.TryParse(combatant.Job, true, out Job job)) - { - barColor = this.BarColorsConfig.GetColor(job); - } - else - { - barColor = this.BarColorsConfig.UKNColor; - } + barColor = this.BarColorsConfig.GetColor(job); } else { - barColor = this.BarConfig.BarColor; + barColor = this.BarColorsConfig.UKNColor; } - - localPos = this.BarConfig.DrawBar(drawList, localPos, size, combatant, barColor, top, current); } + else + { + barColor = this.BarConfig.BarColor; + } + + localPos = this.BarConfig.DrawBar(drawList, localPos, size, combatant, barColor, top, current); } } } @@ -259,31 +251,21 @@ private List GetSortedCombatants(ACTEvent actEvent, MeterDataType dat sortedCombatants.Sort((x, y) => { - string xData = dataType switch + float xFloat = dataType switch { - MeterDataType.Damage => x.DamageTotal, - MeterDataType.Healing => x.HealingTotal, - MeterDataType.DamageTaken => x.DamageTaken, - _ => x.DamageTotal + MeterDataType.Damage => x.DamageTotal?.Value ?? 0, + MeterDataType.Healing => x.HealingTotal?.Value ?? 0, + MeterDataType.DamageTaken => x.DamageTaken?.Value ?? 0, + _ => 0 }; - string yData = dataType switch + float yFloat = dataType switch { - MeterDataType.Damage => y.DamageTotal, - MeterDataType.Healing => y.HealingTotal, - MeterDataType.DamageTaken => y.DamageTaken, - _ => y.DamageTotal + MeterDataType.Damage => y.DamageTotal?.Value ?? 0, + MeterDataType.Healing => y.HealingTotal?.Value ?? 0, + MeterDataType.DamageTaken => y.DamageTaken?.Value ?? 0, + _ => 0 }; - - if (!float.TryParse(yData, NumberStyles.Float, CultureInfo.InvariantCulture, out float yFloat)) - { - return -1; - } - - if (!float.TryParse(xData, NumberStyles.Float, CultureInfo.InvariantCulture, out float xFloat)) - { - return 1; - } return (int)(yFloat - xFloat); }); diff --git a/LMeter/PluginManager.cs b/LMeter/PluginManager.cs index 8add74d..30fa9d3 100644 --- a/LMeter/PluginManager.cs +++ b/LMeter/PluginManager.cs @@ -29,9 +29,7 @@ public class PluginManager : ILMeterDisposable private readonly Vector2 _origin = ImGui.GetMainViewport().Size / 2f; - private readonly Vector2 _configSize = new Vector2(550, 400); - - private DateTime? LastCombatTime { get; set; } = null; + private readonly Vector2 _configSize = new Vector2(550, 550); private readonly ImGuiWindowFlags _mainWindowFlags = ImGuiWindowFlags.NoTitleBar | @@ -64,7 +62,8 @@ public PluginManager( { HelpMessage = "Opens the LMeter configuration window.\n" + "/lm end → Ends current ACT Encounter.\n" - + "/lm clear → Clears all ACT encounter log data.", + + "/lm clear → Clears all ACT encounter log data.\n" + + "/lm toggle → Toggles visibility for the given profile.", ShowInHelp = true } ); @@ -83,17 +82,8 @@ private void Draw() this.WindowSystem.Draw(); - if (this.Config.ACTConfig.AutoEnd && - CharacterState.IsInCombat()) - { - this.LastCombatTime = DateTime.UtcNow; - } - else if (this.LastCombatTime is not null && - this.LastCombatTime < DateTime.UtcNow - TimeSpan.FromSeconds(this.Config.ACTConfig.AutoEndDelay)) - { - ACTClient.EndEncounter(); - this.LastCombatTime = null; - } + this.Config.ACTConfig.TryReconnect(); + this.Config.ACTConfig.TryEndEncounter(); ImGuiHelpers.ForceNextWindowMainViewport(); ImGui.SetNextWindowPos(Vector2.Zero); @@ -109,9 +99,9 @@ private void Draw() ImGui.End(); } - public void Clear() + public void Clear(bool clearAct = false) { - ACTClient.ClearAct(); + ACTClient.Clear(clearAct); foreach (var meter in this.Config.MeterList.Meters) { meter.Clear(); @@ -144,7 +134,12 @@ private void PluginCommand(string command, string arguments) ACTClient.EndEncounter(); break; case "clear": - this.Clear(); + this.Clear(this.Config.ACTConfig.ClearACT); + break; + case { } argument when argument.StartsWith("toggle"): + string[] args = argument.Split(" "); + if (args.Length > 1 && int.TryParse(args[1], out int num)) + this.ToggleMeter(num - 1); break; default: this.ToggleWindow(); @@ -152,6 +147,11 @@ private void PluginCommand(string command, string arguments) } } + private void ToggleMeter(int meterIndex) + { + this.Config.MeterList.ToggleMeter(meterIndex); + } + private void ToggleWindow() { if (this.ConfigRoot.IsOpen) diff --git a/LMeter/changelog.md b/LMeter/changelog.md index 8dcb11e..61d9718 100644 --- a/LMeter/changelog.md +++ b/LMeter/changelog.md @@ -1,3 +1,11 @@ +# Version 0.1.4.0 +- Added advanced text-tag formatting (kilo-format and decimal-format) +- Added text command to show/hide Meters (/lm toggle) +- Added option to hide Meter if ACT is not connected +- Added option to automatically attempt to reconnect to ACT +- Added option to add gaps between bars +- Fixed various bugs + # Version 0.1.3.1 - Make auto-end disabled by default From a7c528a3f2cd8a4392c7358c729fd0bf3858d7b4 Mon Sep 17 00:00:00 2001 From: Lichie Date: Tue, 14 Dec 2021 08:18:58 -0800 Subject: [PATCH 2/5] config defaults, misc stuff --- LMeter/Config/ACTConfig.cs | 2 + LMeter/Config/AboutPage.cs | 5 +- LMeter/Config/BarColorsConfig.cs | 6 +- LMeter/Config/BarConfig.cs | 14 +++- LMeter/Config/FontConfig.cs | 6 +- LMeter/Config/GeneralConfig.cs | 6 +- LMeter/Config/HeaderConfig.cs | 16 ++++- LMeter/Config/IConfigPage.cs | 2 + LMeter/Config/MeterListConfig.cs | 6 +- LMeter/Config/VisibilityConfig.cs | 6 +- LMeter/Helpers/CharacterState.cs | 104 ++++++++++++------------------ LMeter/Meter/MeterWindow.cs | 14 +--- LMeter/Windows/ConfigWindow.cs | 17 ++++- LMeter/changelog.md | 1 + 14 files changed, 111 insertions(+), 94 deletions(-) diff --git a/LMeter/Config/ACTConfig.cs b/LMeter/Config/ACTConfig.cs index 6f2c55d..4cd7a22 100644 --- a/LMeter/Config/ACTConfig.cs +++ b/LMeter/Config/ACTConfig.cs @@ -20,6 +20,8 @@ public class ACTConfig : IConfigPage private DateTime? LastReconnectAttempt { get; set; } = null; public string Name => "ACT"; + + public IConfigPage GetDefault() => new ACTConfig(); public string ACTSocketAddress; diff --git a/LMeter/Config/AboutPage.cs b/LMeter/Config/AboutPage.cs index f223ccb..56f9878 100644 --- a/LMeter/Config/AboutPage.cs +++ b/LMeter/Config/AboutPage.cs @@ -9,6 +9,8 @@ public class AboutPage : IConfigPage public string Name => "About"; + public IConfigPage GetDefault() => new AboutPage(); + public void DrawConfig(Vector2 size, float padX, float padY) { if (ImGui.BeginChild("##AboutPage", new Vector2(size.X, size.Y), true)) @@ -63,8 +65,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) } ImGui.PopStyleVar(); - ImGui.EndChild(); } + + ImGui.EndChild(); } } } diff --git a/LMeter/Config/BarColorsConfig.cs b/LMeter/Config/BarColorsConfig.cs index e9fdd34..c218b27 100644 --- a/LMeter/Config/BarColorsConfig.cs +++ b/LMeter/Config/BarColorsConfig.cs @@ -8,6 +8,8 @@ public class BarColorsConfig : IConfigPage { public string Name => "Colors"; + public IConfigPage GetDefault() => new BarColorsConfig(); + public ConfigColor PLDColor = new ConfigColor(168f / 255f, 210f / 255f, 230f / 255f, 1f); public ConfigColor DRKColor = new ConfigColor(209f / 255f, 38f / 255f, 204f / 255f, 1f); public ConfigColor WARColor = new ConfigColor(207f / 255f, 38f / 255f, 33f / 255f, 1f); @@ -210,9 +212,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) vector = ACNColor.Vector; ImGui.ColorEdit4("ACN", ref vector, ImGuiColorEditFlags.AlphaPreview | ImGuiColorEditFlags.AlphaBar); this.ACNColor.Vector = vector; - - ImGui.EndChild(); } + + ImGui.EndChild(); } } } diff --git a/LMeter/Config/BarConfig.cs b/LMeter/Config/BarConfig.cs index 3f6a309..d0ba816 100644 --- a/LMeter/Config/BarConfig.cs +++ b/LMeter/Config/BarConfig.cs @@ -39,6 +39,16 @@ public class BarConfig : IConfigPage public ConfigColor BarDataOutlineColor = new ConfigColor(0, 0, 0, 0.5f); public string BarDataFontKey = FontsManager.DalamudFontKey; public int BarDataFontId = 0; + + public IConfigPage GetDefault() + { + BarConfig defaultConfig = new BarConfig(); + defaultConfig.BarNameFontKey = FontsManager.DefaultSmallFontKey; + defaultConfig.BarNameFontId = Singletons.Get().GetFontIndex(FontsManager.DefaultSmallFontKey); + defaultConfig.BarDataFontKey = FontsManager.DefaultSmallFontKey; + defaultConfig.BarDataFontId = Singletons.Get().GetFontIndex(FontsManager.DefaultSmallFontKey); + return defaultConfig; + } public Vector2 DrawBar( ImDrawListPtr drawList, @@ -201,9 +211,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.ColorEdit4("Outline Color##Data", ref vector, ImGuiColorEditFlags.AlphaPreview | ImGuiColorEditFlags.AlphaBar); this.BarDataOutlineColor.Vector = vector; } - - ImGui.EndChild(); } + + ImGui.EndChild(); } } } diff --git a/LMeter/Config/FontConfig.cs b/LMeter/Config/FontConfig.cs index 49dff1a..5a8dedd 100644 --- a/LMeter/Config/FontConfig.cs +++ b/LMeter/Config/FontConfig.cs @@ -12,6 +12,8 @@ namespace LMeter.Config public class FontConfig : IConfigPage { public string Name => "Fonts"; + + public IConfigPage GetDefault() => new FontConfig(); [JsonIgnore] private static string? _fontPath = FontsManager.GetUserFontPath(); [JsonIgnore] private int _selectedFont = 0; @@ -140,9 +142,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.EndTable(); } } - - ImGui.EndChild(); } + + ImGui.EndChild(); } public void RefreshFontList() diff --git a/LMeter/Config/GeneralConfig.cs b/LMeter/Config/GeneralConfig.cs index 0343c2e..86a320e 100644 --- a/LMeter/Config/GeneralConfig.cs +++ b/LMeter/Config/GeneralConfig.cs @@ -23,6 +23,8 @@ public class GeneralConfig : IConfigPage public bool Preview = false; public string Name => "General"; + + public IConfigPage GetDefault() => new GeneralConfig(); public Vector2 Position = Vector2.Zero; public Vector2 Size = ImGui.GetMainViewport().Size / 10; @@ -66,9 +68,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.Combo("Sort Type", ref Unsafe.As(ref this.DataType), _meterTypeOptions, _meterTypeOptions.Length); ImGui.Checkbox("Preview", ref this.Preview); - - ImGui.EndChild(); } + + ImGui.EndChild(); } } } diff --git a/LMeter/Config/HeaderConfig.cs b/LMeter/Config/HeaderConfig.cs index 2608e1f..4f8ee29 100644 --- a/LMeter/Config/HeaderConfig.cs +++ b/LMeter/Config/HeaderConfig.cs @@ -44,6 +44,18 @@ public class HeaderConfig : IConfigPage public string StatsFontKey = FontsManager.DalamudFontKey; public string StatsFormat = "[dps]rdps [hps]rhps Deaths: [deaths] "; public bool ThousandsSeparators = true; + + public IConfigPage GetDefault() + { + HeaderConfig defaultConfig = new HeaderConfig(); + defaultConfig.DurationFontKey = FontsManager.DefaultSmallFontKey; + defaultConfig.DurationFontId = Singletons.Get().GetFontIndex(FontsManager.DefaultSmallFontKey); + defaultConfig.NameFontKey = FontsManager.DefaultSmallFontKey; + defaultConfig.NameFontId = Singletons.Get().GetFontIndex(FontsManager.DefaultSmallFontKey); + defaultConfig.StatsFontKey = FontsManager.DefaultSmallFontKey; + defaultConfig.StatsFontId = Singletons.Get().GetFontIndex(FontsManager.DefaultSmallFontKey); + return defaultConfig; + } public Vector2 DrawHeader(Vector2 pos, Vector2 size, Encounter? encounter, ImDrawListPtr drawList) { @@ -252,9 +264,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) } } } - - ImGui.EndChild(); } + + ImGui.EndChild(); } } } diff --git a/LMeter/Config/IConfigPage.cs b/LMeter/Config/IConfigPage.cs index 9c696a7..079f28c 100644 --- a/LMeter/Config/IConfigPage.cs +++ b/LMeter/Config/IConfigPage.cs @@ -6,6 +6,8 @@ public interface IConfigPage { string Name { get; } + IConfigPage GetDefault(); + void DrawConfig(Vector2 size, float padX, float padY); } } diff --git a/LMeter/Config/MeterListConfig.cs b/LMeter/Config/MeterListConfig.cs index ab1547f..31f3a26 100644 --- a/LMeter/Config/MeterListConfig.cs +++ b/LMeter/Config/MeterListConfig.cs @@ -17,6 +17,8 @@ public class MeterListConfig : IConfigPage [JsonIgnore] private string _input = string.Empty; public string Name => "Profiles"; + + public IConfigPage GetDefault() => new MeterListConfig(); public List Meters { get; init; } @@ -48,9 +50,9 @@ private void DrawCreateMenu(Vector2 size, float padX) ImGui.SameLine(); DrawHelpers.DrawButton(string.Empty, FontAwesomeIcon.Download, () => ImportMeter(_input), "Import new Meter", buttonSize); ImGui.PopItemWidth(); - - ImGui.EndChild(); } + + ImGui.EndChild(); } private void DrawMeterTable(Vector2 size, float padX) diff --git a/LMeter/Config/VisibilityConfig.cs b/LMeter/Config/VisibilityConfig.cs index 6ba7eb1..56af7e2 100644 --- a/LMeter/Config/VisibilityConfig.cs +++ b/LMeter/Config/VisibilityConfig.cs @@ -14,6 +14,8 @@ namespace LMeter.Config public class VisibilityConfig : IConfigPage { public string Name => "Visibility"; + + public IConfigPage GetDefault() => new VisibilityConfig(); [JsonIgnore] private string _customJobInput = string.Empty; [JsonIgnore] private string _hideIfValueInput = string.Empty; @@ -126,9 +128,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) this.CustomJobList = jobList; } } - - ImGui.EndChild(); } + + ImGui.EndChild(); } } } \ No newline at end of file diff --git a/LMeter/Helpers/CharacterState.cs b/LMeter/Helpers/CharacterState.cs index 03bd486..ae48600 100644 --- a/LMeter/Helpers/CharacterState.cs +++ b/LMeter/Helpers/CharacterState.cs @@ -9,7 +9,7 @@ namespace LMeter.Helpers public static class CharacterState { - private static readonly uint[] GoldSaucerIDs = { 144, 388, 389, 390, 391, 579, 792, 899, 941 }; + private static readonly uint[] GoldenSaucerIDs = { 144, 388, 389, 390, 391, 579, 792, 899, 941 }; public static bool IsCharacterBusy() { @@ -43,7 +43,7 @@ public static bool IsPerforming() public static bool IsInGoldenSaucer() { - return GoldSaucerIDs.Count(id => id == Singletons.Get().TerritoryType) > 0; + return GoldenSaucerIDs.Count(id => id == Singletons.Get().TerritoryType) > 0; } public static bool IsJob(IEnumerable jobs) @@ -59,69 +59,44 @@ public static bool IsJob(IEnumerable jobs) public static List GetJobsForJobType(JobType type) { - if (type == JobType.All) + switch (type) { - return Enum.GetValues(typeof(Job)).Cast().ToList(); + case JobType.All: + return Enum.GetValues(typeof(Job)).Cast().ToList(); + case JobType.Tanks: + return new List() { Job.GLA, Job.MRD, Job.PLD, Job.WAR, Job.DRK, Job.GNB }; + case JobType.Casters: + return new List() { Job.THM, Job.ACN, Job.BLM, Job.SMN, Job.RDM, Job.BLU }; + case JobType.Melee: + return new List() { Job.PGL, Job.LNC, Job.ROG, Job.MNK, Job.DRG, Job.NIN, Job.SAM, Job.RPR }; + case JobType.Ranged: + return new List() { Job.ARC, Job.BRD, Job.MCH, Job.DNC }; + case JobType.Healers: + return new List() { Job.CNJ, Job.WHM, Job.SCH, Job.AST, Job.SGE }; + case JobType.DoH: + return new List() { Job.CRP, Job.BSM, Job.ARM, Job.GSM, Job.LTW, Job.WVR, Job.ALC, Job.CUL }; + case JobType.DoL: + return new List() { Job.MIN, Job.BOT, Job.FSH }; + case JobType.Combat: + List combatList = GetJobsForJobType(JobType.DoW); + combatList.AddRange(GetJobsForJobType(JobType.DoM)); + return combatList; + case JobType.DoW: + List dowList = GetJobsForJobType(JobType.Tanks); + dowList.AddRange(GetJobsForJobType(JobType.Melee)); + dowList.AddRange(GetJobsForJobType(JobType.Ranged)); + return dowList; + case JobType.DoM: + List domList = GetJobsForJobType(JobType.Casters); + domList.AddRange(GetJobsForJobType(JobType.Healers)); + return domList; + case JobType.Crafters: + List crafterList = GetJobsForJobType(JobType.DoH); + crafterList.AddRange(GetJobsForJobType(JobType.DoL)); + return crafterList; + default: + return new List(); } - - if (type == JobType.Tanks) - { - return new List() { Job.GLA, Job.MRD, Job.PLD, Job.WAR, Job.DRK, Job.GNB }; - } - - if (type == JobType.Casters) - { - return new List() { Job.THM, Job.ACN, Job.BLM, Job.SMN, Job.RDM, Job.BLU }; - } - - if (type == JobType.Melee) - { - return new List() { Job.PGL, Job.LNC, Job.ROG, Job.MNK, Job.DRG, Job.NIN, Job.SAM, Job.RPR }; - } - - if (type == JobType.Ranged) - { - return new List() { Job.ARC, Job.BRD, Job.MCH, Job.DNC }; - } - - if (type == JobType.Healers) - { - return new List() { Job.CNJ, Job.WHM, Job.SCH, Job.AST, Job.SGE }; - } - - if (type == JobType.DoH) - { - return new List() { Job.CRP, Job.BSM, Job.ARM, Job.GSM, Job.LTW, Job.WVR, Job.ALC, Job.CUL }; - } - - if (type == JobType.DoL) - { - return new List() { Job.MIN, Job.BOT, Job.FSH }; - } - - if (type == JobType.DoW) - { - List jobList = GetJobsForJobType(JobType.Tanks); - jobList.AddRange(GetJobsForJobType(JobType.Melee)); - jobList.AddRange(GetJobsForJobType(JobType.Ranged)); - return jobList; - } - - if (type == JobType.DoM) - { - List jobList = GetJobsForJobType(JobType.Casters); - jobList.AddRange(GetJobsForJobType(JobType.Healers)); - return jobList; - } - - if (type == JobType.Crafters) - { - List jobList = GetJobsForJobType(JobType.DoH); - jobList.AddRange(GetJobsForJobType(JobType.DoL)); - return jobList; - } - - return new List(); } } @@ -188,6 +163,7 @@ public enum JobType DoM, Crafters, DoH, - DoL + DoL, + Combat } } diff --git a/LMeter/Meter/MeterWindow.cs b/LMeter/Meter/MeterWindow.cs index 2b77834..826dbb9 100644 --- a/LMeter/Meter/MeterWindow.cs +++ b/LMeter/Meter/MeterWindow.cs @@ -90,18 +90,8 @@ public void ImportPage(IConfigPage page) public static MeterWindow GetDefaultMeter(string name) { MeterWindow newMeter = new MeterWindow(name); - newMeter.HeaderConfig.DurationFontKey = FontsManager.DefaultSmallFontKey; - newMeter.HeaderConfig.DurationFontId = Singletons.Get().GetFontIndex(FontsManager.DefaultSmallFontKey); - newMeter.HeaderConfig.NameFontKey = FontsManager.DefaultSmallFontKey; - newMeter.HeaderConfig.NameFontId = Singletons.Get().GetFontIndex(FontsManager.DefaultSmallFontKey); - newMeter.HeaderConfig.StatsFontKey = FontsManager.DefaultSmallFontKey; - newMeter.HeaderConfig.StatsFontId = Singletons.Get().GetFontIndex(FontsManager.DefaultSmallFontKey); - - newMeter.BarConfig.BarNameFontKey = FontsManager.DefaultSmallFontKey; - newMeter.BarConfig.BarNameFontId = Singletons.Get().GetFontIndex(FontsManager.DefaultSmallFontKey); - newMeter.BarConfig.BarDataFontKey = FontsManager.DefaultSmallFontKey; - newMeter.BarConfig.BarDataFontId = Singletons.Get().GetFontIndex(FontsManager.DefaultSmallFontKey); - + newMeter.HeaderConfig = (HeaderConfig)newMeter.HeaderConfig.GetDefault(); + newMeter.BarConfig = (BarConfig)newMeter.BarConfig.GetDefault(); return newMeter; } diff --git a/LMeter/Windows/ConfigWindow.cs b/LMeter/Windows/ConfigWindow.cs index d4ef523..914bb1e 100644 --- a/LMeter/Windows/ConfigWindow.cs +++ b/LMeter/Windows/ConfigWindow.cs @@ -129,10 +129,13 @@ private void DrawNavBar(IConfigPage? openPage, Vector2 size, float padX) } // calculate empty horizontal space based on size of buttons and text box - float offset = size.X - buttonsize.X * 4 - textInputWidth - padX * 6; + float offset = size.X - buttonsize.X * 5 - textInputWidth - padX * 7; ImGui.SetCursorPosX(ImGui.GetCursorPosX() + offset); + DrawHelpers.DrawButton(string.Empty, FontAwesomeIcon.UndoAlt, () => Reset(openPage), $"Reset {openPage?.Name} to Defaults", buttonsize); + ImGui.SameLine(); + ImGui.PushItemWidth(textInputWidth); if (ImGui.InputText("##Input", ref _name, 64, ImGuiInputTextFlags.EnterReturnsTrue)) { @@ -147,15 +150,23 @@ private void DrawNavBar(IConfigPage? openPage, Vector2 size, float padX) ImGui.PopItemWidth(); ImGui.SameLine(); - DrawHelpers.DrawButton(string.Empty, FontAwesomeIcon.Upload, () => Export(openPage), $"Export {openPage?.Name ?? ""}", buttonsize); + DrawHelpers.DrawButton(string.Empty, FontAwesomeIcon.Upload, () => Export(openPage), $"Export {openPage?.Name}", buttonsize); ImGui.SameLine(); - DrawHelpers.DrawButton(string.Empty, FontAwesomeIcon.Download, () => Import(), $"Import {openPage?.Name ?? ""}", buttonsize); + DrawHelpers.DrawButton(string.Empty, FontAwesomeIcon.Download, () => Import(), $"Import {openPage?.Name}", buttonsize); } ImGui.EndChild(); } + private void Reset(IConfigPage? openPage) + { + if (openPage is not null) + { + this.ConfigStack.Peek().ImportPage(openPage.GetDefault()); + } + } + private void Export(IConfigPage? openPage) { if (openPage is not null) diff --git a/LMeter/changelog.md b/LMeter/changelog.md index 61d9718..3369d2d 100644 --- a/LMeter/changelog.md +++ b/LMeter/changelog.md @@ -4,6 +4,7 @@ - Added option to hide Meter if ACT is not connected - Added option to automatically attempt to reconnect to ACT - Added option to add gaps between bars +- Added "Combat" job group to Visibility settings - Fixed various bugs # Version 0.1.3.1 From a659793e9fea72ec136ff00a1acd2bd711dd7406 Mon Sep 17 00:00:00 2001 From: Lichie Date: Wed, 15 Dec 2021 00:22:53 -0800 Subject: [PATCH 3/5] new tags, fix header, lots of refactoring. --- LMeter/ACT/ACTEvent.cs | 115 ++++++++++++++------------- LMeter/ACT/LazyFloat.cs | 2 +- LMeter/ACT/LazyString.cs | 128 +++++++++++++++++++++++++++++++ LMeter/ACT/TextTagFormatter.cs | 34 +++++--- LMeter/Config/AboutPage.cs | 2 +- LMeter/Config/BarConfig.cs | 32 ++++---- LMeter/Config/HeaderConfig.cs | 45 +++++++---- LMeter/Config/MeterListConfig.cs | 30 +++++--- LMeter/Helpers/CharacterState.cs | 10 ++- LMeter/Helpers/Utils.cs | 15 +++- LMeter/Meter/MeterWindow.cs | 87 ++++++++++----------- LMeter/PluginManager.cs | 93 +++++++++++----------- LMeter/changelog.md | 6 +- 13 files changed, 385 insertions(+), 214 deletions(-) create mode 100644 LMeter/ACT/LazyString.cs diff --git a/LMeter/ACT/ACTEvent.cs b/LMeter/ACT/ACTEvent.cs index 1cc1403..d08eeb5 100644 --- a/LMeter/ACT/ACTEvent.cs +++ b/LMeter/ACT/ACTEvent.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; using System; using LMeter.Helpers; -using System.Text.RegularExpressions; +using Newtonsoft.Json.Converters; namespace LMeter.ACT { @@ -14,16 +14,16 @@ public class ACTEvent public DateTime Timestamp; [JsonProperty("type")] - public string EventType { get; private set; } = string.Empty; + public string EventType = string.Empty; [JsonProperty("isActive")] - public string IsActive { get; private set; } = string.Empty; + public string IsActive = string.Empty; [JsonProperty("Encounter")] - public Encounter Encounter { get; private set; } = new Encounter(); + public Encounter? Encounter; [JsonProperty("Combatant")] - public Dictionary Combatants { get; private set; } = new Dictionary(); + public Dictionary? Combatants; public bool IsEncounterActive() => bool.TryParse(this.IsActive, out bool active) && active; @@ -40,58 +40,53 @@ public static ACTEvent GetTestData() public class Encounter { [JsonIgnore] - private static readonly Random _rand = new Random(); + public static string[] TextTags { get; } = typeof(Encounter).GetFields().Select(x => $"[{x.Name.ToLower()}]").ToArray(); [JsonIgnore] - private static readonly Regex _regex = new Regex(@"\[(\w*)(-k)?\.?(\d+)?\]", RegexOptions.Compiled); + private static readonly Random _rand = new Random(); [JsonIgnore] - private static readonly Dictionary _properties = typeof(Encounter).GetProperties().ToDictionary((x) => x.Name.ToLower()); - - public static string[] GetTags() - { - return typeof(Encounter).GetProperties().Select(x => $"[{x.Name.ToLower()}]").ToArray(); - } + private static readonly Dictionary _fields = typeof(Encounter).GetFields().ToDictionary((x) => x.Name.ToLower()); public string GetFormattedString(string format, string numberFormat) { - return _regex.Replace(format, new TextTagFormatter(this, numberFormat, _properties).Evaluate); + return TextTagFormatter.TextTagRegex.Replace(format, new TextTagFormatter(this, numberFormat, _fields).Evaluate); } [JsonProperty("title")] - public string Title { get; private set; } = string.Empty; + public string Title = string.Empty; [JsonProperty("duration")] - public string Duration { get; private set; } = string.Empty; + public string Duration = string.Empty; [JsonProperty("DURATION")] - private string _duration { get; set; } = string.Empty; + private string _duration = string.Empty; [JsonProperty("encdps")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? Dps { get; private set; } + public LazyFloat? Dps; [JsonProperty("damage")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? DamageTotal { get; private set; } + public LazyFloat? DamageTotal; [JsonProperty("enchps")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? Hps { get; private set; } + public LazyFloat? Hps; [JsonProperty("healed")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? HealingTotal { get; private set; } + public LazyFloat? HealingTotal; [JsonProperty("damagetaken")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? DamageTaken { get; private set; } + public LazyFloat? DamageTaken; [JsonProperty("deaths")] - public string? Deaths { get; private set; } + public string? Deaths; [JsonProperty("kills")] - public string? Kills { get; private set; } + public string? Kills; public static Encounter GetTestData() { @@ -114,87 +109,99 @@ public static Encounter GetTestData() public class Combatant { [JsonIgnore] - private static readonly Random _rand = new Random(); + public static string[] TextTags { get; } = typeof(Combatant).GetFields().Select(x => $"[{x.Name.ToLower()}]").ToArray(); [JsonIgnore] - private static readonly Regex _regex = new Regex(@"\[(\w*)(-k)?\.?(\d+)?\]", RegexOptions.Compiled); + private static readonly Random _rand = new Random(); [JsonIgnore] - private static readonly Dictionary _properties = typeof(Combatant).GetProperties().ToDictionary((x) => x.Name.ToLower()); - - public static string[] GetTags() - { - return typeof(Combatant).GetProperties().Select(x => $"[{x.Name.ToLower()}]").ToArray(); - } + private static readonly Dictionary _fields = typeof(Combatant).GetFields().ToDictionary((x) => x.Name.ToLower()); public string GetFormattedString(string format, string numberFormat) { - return _regex.Replace(format, new TextTagFormatter(this, numberFormat, _properties).Evaluate); + return TextTagFormatter.TextTagRegex.Replace(format, new TextTagFormatter(this, numberFormat, _fields).Evaluate); } [JsonProperty("name")] - public string Name { get; private set; } = string.Empty; + public string Name = string.Empty; + + [JsonIgnore] + public LazyString? Name_First; + + [JsonIgnore] + public LazyString? Name_Last; [JsonProperty("job")] - public string Job { get; private set; } = string.Empty; + [JsonConverter(typeof(StringEnumConverter))] + public Job Job; + + [JsonIgnore] + public LazyString? JobName; [JsonProperty("duration")] - public string Duration { get; private set; } = string.Empty; + public string Duration = string.Empty; [JsonProperty("encdps")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? EncDps { get; private set; } + public LazyFloat? EncDps; [JsonProperty("dps")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? Dps { get; private set; } + public LazyFloat? Dps; [JsonProperty("damage")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? DamageTotal { get; private set; } + public LazyFloat? DamageTotal; [JsonProperty("damage%")] - public string DamagePct { get; private set; } = string.Empty; + public string DamagePct = string.Empty; [JsonProperty("crithit%")] - public string CritHitPct { get; private set; } = string.Empty; + public string CritHitPct = string.Empty; [JsonProperty("DirectHitPct")] - public string DirectHitPct { get; private set; } = string.Empty; + public string DirectHitPct = string.Empty; [JsonProperty("CritDirectHitPct")] - public string CritDirectHitPct { get; private set; } = string.Empty; + public string CritDirectHitPct = string.Empty; [JsonProperty("enchps")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? EncHps { get; private set; } + public LazyFloat? EncHps; [JsonProperty("hps")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? Hps { get; private set; } + public LazyFloat? Hps; [JsonProperty("healed")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? HealingTotal { get; private set; } + public LazyFloat? HealingTotal; [JsonProperty("healed%")] - public string HealingPct { get; private set; } = string.Empty; + public string HealingPct = string.Empty; [JsonProperty("damagetaken")] [JsonConverter(typeof(LazyFloatConverter))] - public LazyFloat? DamageTaken { get; private set; } + public LazyFloat? DamageTaken; [JsonProperty("deaths")] - public string Deaths { get; private set; } = string.Empty; + public string Deaths = string.Empty; [JsonProperty("kills")] - public string Kills { get; private set; } = string.Empty; + public string Kills = string.Empty; [JsonProperty("maxhit")] - public string MaxHit { get; private set; } = string.Empty; + public string MaxHit = string.Empty; [JsonProperty("MAXHIT")] - private string _maxHit { get; set; } = string.Empty; + private string _maxHit = string.Empty; + + public Combatant() + { + this.Name_First = new LazyString(() => this.Name, LazyStringConverters.FirstName); + this.Name_Last = new LazyString(() => this.Name, LazyStringConverters.LastName); + this.JobName = new LazyString(() => this.Job, LazyStringConverters.JobName); + } public static Dictionary GetTestData() { @@ -220,9 +227,9 @@ private static Combatant GetCombatant(params string[] jobs) return new Combatant() { - Name = "Fake Name", + Name = "Firstname Lastname", Duration = "00:30", - Job = jobs.Select(x => x.ToString()).ElementAt(_rand.Next(jobs.Length)), + Job = Enum.Parse(jobs[_rand.Next(jobs.Length)]), DamageTotal = new LazyFloat(damage.ToString()), Dps = new LazyFloat((damage / 30).ToString()), EncDps = new LazyFloat((damage / 30).ToString()), diff --git a/LMeter/ACT/LazyFloat.cs b/LMeter/ACT/LazyFloat.cs index 82cf1b6..7dcd5fe 100644 --- a/LMeter/ACT/LazyFloat.cs +++ b/LMeter/ACT/LazyFloat.cs @@ -15,7 +15,7 @@ public float Value { get { - if(this.WasGenerated) + if (this.WasGenerated) { return this._value; } diff --git a/LMeter/ACT/LazyString.cs b/LMeter/ACT/LazyString.cs new file mode 100644 index 0000000..d65aaa6 --- /dev/null +++ b/LMeter/ACT/LazyString.cs @@ -0,0 +1,128 @@ + +using System; +using LMeter.Helpers; + +namespace LMeter.ACT +{ + public class LazyString + { + private string _value = string.Empty; + private Func _generator; + private Func _getInput; + + public bool WasGenerated { get; private set; } + + public string Value + { + get + { + if (this.WasGenerated) + { + return this._value; + } + + this._value = this._generator.Invoke(this._getInput.Invoke()); + this.WasGenerated = true; + return this._value; + } + } + + public LazyString(Func getInput, Func generator) + { + this._getInput = getInput; + this._generator = generator; + } + + public override string? ToString() + { + return this.Value; + } + } + + public static class LazyStringConverters + { + public static string FirstName(string? input) + { + if (string.IsNullOrWhiteSpace(input)) + { + return string.Empty; + } + + string[] splits = input.Split(" "); + if (splits.Length < 2) + { + return input; + } + + return splits[0]; + } + + public static string LastName(string? input) + { + if (string.IsNullOrWhiteSpace(input)) + { + return string.Empty; + } + + string[] splits = input.Split(" "); + if (splits.Length < 2) + { + return input; + } + + return splits[1]; + } + + public static string JobName(Job input) => input switch + { + Job.GLA => "Gladiator", + Job.MRD => "Marauder", + Job.PLD => "Paladin", + Job.WAR => "Warrior", + Job.DRK => "Dark Knight", + Job.GNB => "Gunbreaker", + + Job.CNJ => "Conjurer", + Job.WHM => "White Mage", + Job.SCH => "Scholar", + Job.AST => "Astrologian", + Job.SGE => "Sage", + + Job.PGL => "Pugilist", + Job.LNC => "Lancer", + Job.ROG => "Rogue", + Job.MNK => "Monk", + Job.DRG => "Dragoon", + Job.NIN => "Ninja", + Job.SAM => "Samurai", + Job.RPR => "Reaper", + + Job.ARC => "Archer", + Job.BRD => "Bard", + Job.MCH => "Machinist", + Job.DNC => "Dancer", + + Job.THM => "Thaumaturge", + Job.ACN => "Arcanist", + Job.BLM => "Black Mage", + Job.SMN => "Summoner", + Job.RDM => "Red Mage", + Job.BLU => "Blue Mage", + + Job.CRP => "Carpenter", + Job.BSM => "Blacksmith", + Job.ARM => "Armorer", + Job.GSM => "Goldsmith", + Job.LTW => "Leatherworker", + Job.WVR => "Weaver", + Job.ALC => "Alchemist", + Job.CUL => "Culinarian", + + Job.MIN => "Miner", + Job.BOT => "Botanist", + Job.FSH => "Fisher", + + _ => "" + }; + } +} \ No newline at end of file diff --git a/LMeter/ACT/TextTagFormatter.cs b/LMeter/ACT/TextTagFormatter.cs index 2a12260..98fed83 100644 --- a/LMeter/ACT/TextTagFormatter.cs +++ b/LMeter/ACT/TextTagFormatter.cs @@ -1,23 +1,26 @@ using System.Reflection; using System.Collections.Generic; using System.Text.RegularExpressions; +using System; namespace LMeter.ACT { public class TextTagFormatter { - private string Format { get; set; } - private Dictionary Properties { get; set; } - private object Source { get; set; } + public static readonly Regex TextTagRegex = new Regex(@"\[(\w*)(:k)?\.?(\d+)?\]", RegexOptions.Compiled); + + private string _format; + private Dictionary _fields; + private object _source; public TextTagFormatter( object source, string format, - Dictionary properties) + Dictionary fields) { - this.Source = source; - this.Format = format; - this.Properties = properties; + this._source = source; + this._format = format; + this._fields = fields; } public string Evaluate(Match m) @@ -28,15 +31,20 @@ public string Evaluate(Match m) } string format = string.IsNullOrEmpty(m.Groups[3].Value) - ? $"{this.Format}0" - : $"{this.Format}{m.Groups[3].Value}"; + ? $"{this._format}0" + : $"{this._format}{m.Groups[3].Value}"; string? value = null; string key = m.Groups[1].Value; - if (this.Properties.ContainsKey(key)) + if (this._fields.ContainsKey(key)) { - object? propValue = this.Properties[m.Groups[1].Value].GetValue(this.Source); + object? propValue = this._fields[m.Groups[1].Value].GetValue(this._source); + + if (propValue is null) + { + return string.Empty; + } if (propValue is LazyFloat lazyFloat) { @@ -45,7 +53,9 @@ public string Evaluate(Match m) } else { - value = propValue?.ToString(); + value = int.TryParse(m.Groups[3].Value, out int trim) + ? propValue?.ToString().AsSpan(0, trim).ToString() + : propValue?.ToString(); } } diff --git a/LMeter/Config/AboutPage.cs b/LMeter/Config/AboutPage.cs index 56f9878..586e0a3 100644 --- a/LMeter/Config/AboutPage.cs +++ b/LMeter/Config/AboutPage.cs @@ -7,7 +7,7 @@ namespace LMeter.Config public class AboutPage : IConfigPage { - public string Name => "About"; + public string Name => "Changelog"; public IConfigPage GetDefault() => new AboutPage(); diff --git a/LMeter/Config/BarConfig.cs b/LMeter/Config/BarConfig.cs index d0ba816..fcc7fb5 100644 --- a/LMeter/Config/BarConfig.cs +++ b/LMeter/Config/BarConfig.cs @@ -25,7 +25,7 @@ public class BarConfig : IConfigPage public bool UseJobColor = true; public ConfigColor BarColor = new ConfigColor(.3f, .3f, .3f, 1f); - public string BarNameFormat = " [name]"; + public string LeftTextFormat = "[name]"; public ConfigColor BarNameColor = new ConfigColor(1, 1, 1, 1); public bool BarNameShowOutline = true; public ConfigColor BarNameOutlineColor = new ConfigColor(0, 0, 0, 0.5f); @@ -33,7 +33,7 @@ public class BarConfig : IConfigPage public int BarNameFontId = 0; public bool UseCharacterName = false; - public string BarDataFormat = "[damagetotal-k.1] ([encdps-k.1], [damagepct]) "; + public string RightTextFormat = "[damagetotal:k.1] ([encdps:k.1], [damagepct])"; public ConfigColor BarDataColor = new ConfigColor(1, 1, 1, 1); public bool BarDataShowOutline = true; public ConfigColor BarDataOutlineColor = new ConfigColor(0, 0, 0, 0.5f); @@ -64,26 +64,26 @@ public Vector2 DrawBar( Vector2 barFillSize = new Vector2(size.X * (current / top), barHeight); drawList.AddRectFilled(localPos, localPos + barFillSize, barColor.Base); - if (this.ShowJobIcon && Enum.TryParse(combatant.Job, true, out Job job)) + if (this.ShowJobIcon && combatant.Job != Job.UKN) { - uint jobIconId = 62000u + (uint)job + 100u * (uint)this.JobIconStyle; + uint jobIconId = 62000u + (uint)combatant.Job + 100u * (uint)this.JobIconStyle; Vector2 jobIconSize = Vector2.One * barHeight; DrawHelpers.DrawIcon(jobIconId, localPos + this.JobIconOffset, jobIconSize, drawList); } bool fontPushed = FontsManager.PushFont(this.BarNameFontKey); - string nameText = combatant.GetFormattedString(this.BarNameFormat, this.ThousandsSeparators ? "N" : "F"); + string leftText = combatant.GetFormattedString($" {this.LeftTextFormat} ", this.ThousandsSeparators ? "N" : "F"); if (this.UseCharacterName && combatant.Name.Contains("YOU")) { string characterName = Singletons.Get().LocalPlayer?.Name.ToString() ?? "YOU"; - nameText = nameText.Replace("YOU", characterName); + leftText = leftText.Replace("YOU", characterName); } - Vector2 nameTextSize = ImGui.CalcTextSize(nameText); + Vector2 nameTextSize = ImGui.CalcTextSize(leftText); Vector2 namePos = Utils.GetAnchoredPosition(localPos, -barSize, DrawAnchor.Left); namePos = Utils.GetAnchoredPosition(namePos, nameTextSize, DrawAnchor.Left); DrawHelpers.DrawText(drawList, - nameText, + leftText, namePos.AddX(this.ShowJobIcon ? barHeight : 5), this.BarNameColor.Base, this.BarNameShowOutline, @@ -95,12 +95,12 @@ public Vector2 DrawBar( } fontPushed = FontsManager.PushFont(this.BarDataFontKey); - string dataText = combatant.GetFormattedString(this.BarDataFormat, this.ThousandsSeparators ? "N" : "F"); - Vector2 dataTextSize = ImGui.CalcTextSize(dataText); + string rightText = combatant.GetFormattedString($" {this.RightTextFormat} ", this.ThousandsSeparators ? "N" : "F"); + Vector2 dataTextSize = ImGui.CalcTextSize(rightText); Vector2 dataPos = Utils.GetAnchoredPosition(localPos, -barSize, DrawAnchor.Right); dataPos = Utils.GetAnchoredPosition(dataPos, dataTextSize, DrawAnchor.Right); DrawHelpers.DrawText(drawList, - dataText, + rightText, dataPos, this.BarDataColor.Base, this.BarDataShowOutline, @@ -151,12 +151,11 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.NewLine(); ImGui.Checkbox("Use your name instead of 'YOU'", ref this.UseCharacterName); - ImGui.InputText("Name Format", ref this.BarNameFormat, 128); + ImGui.InputText("Left Text Format", ref this.LeftTextFormat, 128); if (ImGui.IsItemHovered()) { - string tooltip = $"Available Data Tags:\n\n{string.Join("\n", Combatant.GetTags())}"; - ImGui.SetTooltip(tooltip); + ImGui.SetTooltip(Utils.GetTagsTooltip(Combatant.TextTags)); } if (!FontsManager.ValidateFont(fontOptions, this.BarNameFontId, this.BarNameFontKey)) @@ -182,12 +181,11 @@ public void DrawConfig(Vector2 size, float padX, float padY) } ImGui.NewLine(); - ImGui.InputText("Data Format", ref this.BarDataFormat, 128); + ImGui.InputText("Right Text Format", ref this.RightTextFormat, 128); if (ImGui.IsItemHovered()) { - string tooltip = $"Available Data Tags:\n\n{string.Join("\n", Combatant.GetTags())}"; - ImGui.SetTooltip(tooltip); + ImGui.SetTooltip(Utils.GetTagsTooltip(Combatant.TextTags)); } if (!FontsManager.ValidateFont(fontOptions, this.BarDataFontId, this.BarDataFontKey)) diff --git a/LMeter/Config/HeaderConfig.cs b/LMeter/Config/HeaderConfig.cs index 4f8ee29..15e8775 100644 --- a/LMeter/Config/HeaderConfig.cs +++ b/LMeter/Config/HeaderConfig.cs @@ -5,7 +5,6 @@ using Newtonsoft.Json; using System.Runtime.CompilerServices; using LMeter.ACT; -using System.Globalization; namespace LMeter.Config { @@ -24,6 +23,8 @@ public class HeaderConfig : IConfigPage public ConfigColor DurationColor = new ConfigColor(0f / 255f, 190f / 255f, 225f / 255f, 1f); public bool DurationShowOutline = true; public ConfigColor DurationOutlineColor = new ConfigColor(0, 0, 0, 0.5f); + public DrawAnchor DurationAlign = DrawAnchor.Left; + public Vector2 DurationOffset = new Vector2(0, 0); public int DurationFontId = 0; public string DurationFontKey = FontsManager.DalamudFontKey; @@ -31,6 +32,8 @@ public class HeaderConfig : IConfigPage public ConfigColor NameColor = new ConfigColor(1, 1, 1, 1); public bool NameShowOutline = true; public ConfigColor NameOutlineColor = new ConfigColor(0, 0, 0, 0.5f); + public DrawAnchor NameAlign = DrawAnchor.Left; + public Vector2 NameOffset = new Vector2(0, 0); public int NameFontId = 0; public string NameFontKey = FontsManager.DalamudFontKey; @@ -42,7 +45,7 @@ public class HeaderConfig : IConfigPage public Vector2 StatsOffset = new Vector2(0, 0); public int StatsFontId = 0; public string StatsFontKey = FontsManager.DalamudFontKey; - public string StatsFormat = "[dps]rdps [hps]rhps Deaths: [deaths] "; + public string RaidStatsFormat = "[dps]rdps [hps]rhps Deaths: [deaths]"; public bool ThousandsSeparators = true; public IConfigPage GetDefault() @@ -75,7 +78,7 @@ public Vector2 DrawHeader(Vector2 pos, Vector2 size, Encounter? encounter, ImDra string duration = encounter is null ? $" LMeter v{Plugin.Version}" : $" {encounter.Duration}"; durationSize = ImGui.CalcTextSize(duration); durationPos = Utils.GetAnchoredPosition(durationPos, -headerSize, DrawAnchor.Left); - durationPos = Utils.GetAnchoredPosition(durationPos, durationSize, DrawAnchor.Left); + durationPos = Utils.GetAnchoredPosition(durationPos, durationSize, this.DurationAlign) + this.DurationOffset; DrawHelpers.DrawText(drawList, duration, durationPos, this.DurationColor.Base, this.DurationShowOutline, this.DurationOutlineColor.Base); if (fontPushed) { @@ -86,7 +89,7 @@ public Vector2 DrawHeader(Vector2 pos, Vector2 size, Encounter? encounter, ImDra Vector2 raidStatsSize = Vector2.Zero; if (this.ShowRaidStats && encounter is not null) { - string text = encounter.GetFormattedString(this.StatsFormat, this.ThousandsSeparators ? "N" : "F"); + string text = encounter.GetFormattedString($" {this.RaidStatsFormat} ", this.ThousandsSeparators ? "N" : "F"); if (!string.IsNullOrEmpty(text)) { @@ -110,18 +113,18 @@ public Vector2 DrawHeader(Vector2 pos, Vector2 size, Encounter? encounter, ImDra if (durationSize.X + raidStatsSize.X + nameSize.X > size.X) { - float elipsesWidth = ImGui.CalcTextSize("...").X; + float elipsesWidth = ImGui.CalcTextSize("... ").X; do { - name = new string(name.AsSpan(0, name.Length - 1)); + name = name.AsSpan(0, name.Length - 1).ToString(); nameSize = ImGui.CalcTextSize(name); } while (durationSize.X + raidStatsSize.X + nameSize.X + elipsesWidth > size.X); - name += "..."; + name += "... "; } - Vector2 namePos = Utils.GetAnchoredPosition(new Vector2(durationPos.X + durationSize.X, pos.Y), -headerSize, DrawAnchor.Left); - namePos = Utils.GetAnchoredPosition(namePos, nameSize, DrawAnchor.Left); + Vector2 namePos = Utils.GetAnchoredPosition(pos.AddX(durationSize.X), -headerSize, DrawAnchor.Left); + namePos = Utils.GetAnchoredPosition(namePos, nameSize, this.NameAlign) + this.NameOffset; DrawHelpers.DrawText(drawList, name, namePos, this.NameColor.Base, this.NameShowOutline, this.NameOutlineColor.Base); if (fontPushed) { @@ -158,6 +161,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.Checkbox("Show Encounter Duration", ref this.ShowEncounterDuration); if (this.ShowEncounterDuration) { + DrawHelpers.DrawNestIndicator(2); + ImGui.DragFloat2("Position Offset##Duration", ref this.DurationOffset); + if (!FontsManager.ValidateFont(fontOptions, this.DurationFontId, this.DurationFontKey)) { this.DurationFontId = 0; @@ -168,6 +174,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.Combo("Font##Duration", ref this.DurationFontId, fontOptions, fontOptions.Length); this.DurationFontKey = fontOptions[this.DurationFontId]; + DrawHelpers.DrawNestIndicator(2); + ImGui.Combo("Text Align##Duration", ref Unsafe.As(ref this.DurationAlign), _anchorOptions, _anchorOptions.Length); + DrawHelpers.DrawNestIndicator(2); vector = this.DurationColor.Vector; ImGui.ColorEdit4("Text Color##Duration", ref vector, ImGuiColorEditFlags.AlphaPreview | ImGuiColorEditFlags.AlphaBar); @@ -189,6 +198,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.Checkbox("Show Encounter Name", ref this.ShowEncounterName); if (this.ShowEncounterName) { + DrawHelpers.DrawNestIndicator(2); + ImGui.DragFloat2("Position Offset##Name", ref this.NameOffset); + if (!FontsManager.ValidateFont(fontOptions, this.NameFontId, this.NameFontKey)) { this.NameFontId = 0; @@ -199,6 +211,9 @@ public void DrawConfig(Vector2 size, float padX, float padY) ImGui.Combo("Font##Name", ref this.NameFontId, fontOptions, fontOptions.Length); this.NameFontKey = fontOptions[this.NameFontId]; + DrawHelpers.DrawNestIndicator(2); + ImGui.Combo("Text Align##Name", ref Unsafe.As(ref this.NameAlign), _anchorOptions, _anchorOptions.Length); + DrawHelpers.DrawNestIndicator(2); vector = this.NameColor.Vector; ImGui.ColorEdit4("Text Color##Name", ref vector, ImGuiColorEditFlags.AlphaPreview | ImGuiColorEditFlags.AlphaBar); @@ -221,16 +236,14 @@ public void DrawConfig(Vector2 size, float padX, float padY) if (this.ShowRaidStats) { DrawHelpers.DrawNestIndicator(2); - ImGui.InputText("Raid Stats Format", ref this.StatsFormat, 128); - - DrawHelpers.DrawNestIndicator(2); - ImGui.Checkbox("Use Thousands Separators for Numbers", ref this.ThousandsSeparators); - + ImGui.InputText("Raid Stats Format", ref this.RaidStatsFormat, 128); if (ImGui.IsItemHovered()) { - string tooltip = $"Available Data Tags:\n\n{string.Join("\n", Encounter.GetTags())}"; - ImGui.SetTooltip(tooltip); + ImGui.SetTooltip(Utils.GetTagsTooltip(Encounter.TextTags)); } + + DrawHelpers.DrawNestIndicator(2); + ImGui.Checkbox("Use Thousands Separators for Numbers", ref this.ThousandsSeparators); DrawHelpers.DrawNestIndicator(2); ImGui.DragFloat2("Position Offset##Stats", ref this.StatsOffset); diff --git a/LMeter/Config/MeterListConfig.cs b/LMeter/Config/MeterListConfig.cs index 31f3a26..2575490 100644 --- a/LMeter/Config/MeterListConfig.cs +++ b/LMeter/Config/MeterListConfig.cs @@ -17,8 +17,6 @@ public class MeterListConfig : IConfigPage [JsonIgnore] private string _input = string.Empty; public string Name => "Profiles"; - - public IConfigPage GetDefault() => new MeterListConfig(); public List Meters { get; init; } @@ -26,12 +24,30 @@ public MeterListConfig() { this.Meters = new List(); } + + public IConfigPage GetDefault() => new MeterListConfig(); public void DrawConfig(Vector2 size, float padX, float padY) { this.DrawCreateMenu(size, padX); this.DrawMeterTable(size.AddY(-padY), padX); } + + public void ToggleMeter(int meterIndex) + { + if (meterIndex >= 0 && meterIndex < this.Meters.Count) + { + this.Meters[meterIndex].VisibilityConfig.AlwaysHide ^= true; + } + } + + public void ToggleClickThrough(int meterIndex) + { + if (meterIndex >= 0 && meterIndex < this.Meters.Count) + { + this.Meters[meterIndex].GeneralConfig.ClickThrough ^= true; + } + } private void DrawCreateMenu(Vector2 size, float padX) { @@ -132,14 +148,6 @@ private void CreateMeter(string name) this._input = string.Empty; } - - public void ToggleMeter(int meterIndex) - { - if (meterIndex >= 0 && meterIndex < this.Meters.Count) - { - this.Meters[meterIndex].VisibilityConfig.AlwaysHide ^= true; - } - } private void EditMeter(MeterWindow meter) { @@ -154,7 +162,7 @@ private void DeleteMeter(MeterWindow meter) private void ImportMeter(string input) { string importString = input; - if (string.IsNullOrEmpty(importString)) + if (string.IsNullOrWhiteSpace(importString)) { importString = ImGui.GetClipboardText(); } diff --git a/LMeter/Helpers/CharacterState.cs b/LMeter/Helpers/CharacterState.cs index ae48600..c8044ae 100644 --- a/LMeter/Helpers/CharacterState.cs +++ b/LMeter/Helpers/CharacterState.cs @@ -9,7 +9,7 @@ namespace LMeter.Helpers public static class CharacterState { - private static readonly uint[] GoldenSaucerIDs = { 144, 388, 389, 390, 391, 579, 792, 899, 941 }; + private static readonly uint[] _goldenSaucerIDs = { 144, 388, 389, 390, 391, 579, 792, 899, 941 }; public static bool IsCharacterBusy() { @@ -43,7 +43,7 @@ public static bool IsPerforming() public static bool IsInGoldenSaucer() { - return GoldenSaucerIDs.Count(id => id == Singletons.Get().TerritoryType) > 0; + return _goldenSaucerIDs.Any(id => id == Singletons.Get().TerritoryType); } public static bool IsJob(IEnumerable jobs) @@ -102,6 +102,8 @@ public static List GetJobsForJobType(JobType type) public enum Job { + UKN = 0, + GLA = 1, MRD = 3, PLD = 19, @@ -161,9 +163,9 @@ public enum JobType Healers, DoW, DoM, + Combat, Crafters, DoH, - DoL, - Combat + DoL } } diff --git a/LMeter/Helpers/Utils.cs b/LMeter/Helpers/Utils.cs index 406678d..ff1946c 100644 --- a/LMeter/Helpers/Utils.cs +++ b/LMeter/Helpers/Utils.cs @@ -10,7 +10,6 @@ namespace LMeter.Helpers { public static class Utils { - public static Vector2 GetAnchoredPosition(Vector2 position, Vector2 size, DrawAnchor anchor) { return anchor switch @@ -82,6 +81,20 @@ public static void OpenUrl(string url) } } } + + public static string GetTagsTooltip(string[] textTags) + { + return $"Available Text Tags:\n\n{string.Join("\n", textTags)}\n\n" + + "Append the characters ':k' to a numeric tag to kilo-format it.\n" + + "Append a '.' and a number to limit the number of characters,\n" + + "or the number of decimals when used with numeric values.\n\nExamples:\n" + + "[damagetotal] => 123,456\n" + + "[damagetotal:k] => 123k\n" + + "[damagetotal:k.1] => 123.4k\n\n" + + "[name] => Firstname Lastname\n" + + "[name_first.5] => First\n" + + "[name_last.1] => L"; + } } public enum DrawAnchor diff --git a/LMeter/Meter/MeterWindow.cs b/LMeter/Meter/MeterWindow.cs index 826dbb9..a8f39f5 100644 --- a/LMeter/Meter/MeterWindow.cs +++ b/LMeter/Meter/MeterWindow.cs @@ -7,25 +7,24 @@ using LMeter.Helpers; using LMeter.ACT; using Newtonsoft.Json; -using System.Globalization; namespace LMeter.Meter { public class MeterWindow : IConfigurable { [JsonIgnore] public readonly string ID; - [JsonIgnore] private bool LastFrameWasUnlocked = false; - [JsonIgnore] private bool LastFrameWasDragging = false; - [JsonIgnore] private bool LastFrameWasPreview = false; - [JsonIgnore] private bool Unlocked = false; - [JsonIgnore] private bool Hovered = false; - [JsonIgnore] private bool Dragging = false; - [JsonIgnore] private bool Locked = false; - [JsonIgnore] private ACTEvent? PreviewEvent = null; - [JsonIgnore] private int ScrollPosition = 0; - - [JsonIgnore] private DateTime? LastSortedTimestamp = null; - [JsonIgnore] private List LastSortedCombatants = new List(); + + [JsonIgnore] private bool _lastFrameWasUnlocked = false; + [JsonIgnore] private bool _lastFrameWasDragging = false; + [JsonIgnore] private bool _lastFrameWasPreview = false; + [JsonIgnore] private bool _unlocked = false; + [JsonIgnore] private bool _hovered = false; + [JsonIgnore] private bool _dragging = false; + [JsonIgnore] private bool _locked = false; + [JsonIgnore] private ACTEvent? _previewEvent = null; + [JsonIgnore] private int _scrollPosition = 0; + [JsonIgnore] private DateTime? _lastSortedTimestamp = null; + [JsonIgnore] private List _lastSortedCombatants = new List(); public string Name { get; set; } @@ -42,7 +41,7 @@ public class MeterWindow : IConfigurable public MeterWindow(string name) { this.Name = name; - this.ID = $"LMeter_{GetType().Name}_{Guid.NewGuid()}"; + this.ID = $"LMeter_MeterWindow_{Guid.NewGuid()}"; this.GeneralConfig = new GeneralConfig(); this.HeaderConfig = new HeaderConfig(); this.BarConfig = new BarConfig(); @@ -97,18 +96,18 @@ public static MeterWindow GetDefaultMeter(string name) public void Clear() { - this.LastSortedCombatants = new List(); - this.LastSortedTimestamp = null; + this._lastSortedCombatants = new List(); + this._lastSortedTimestamp = null; } // Dont ask protected void UpdateDragData(Vector2 pos, Vector2 size, bool locked) { - this.Unlocked = !locked; - this.Hovered = ImGui.IsMouseHoveringRect(pos, pos + size); - this.Dragging = this.LastFrameWasDragging && ImGui.IsMouseDown(ImGuiMouseButton.Left); - this.Locked = (this.Unlocked && !this.LastFrameWasUnlocked || !this.Hovered) && !this.Dragging; - this.LastFrameWasDragging = this.Hovered || this.Dragging; + this._unlocked = !locked; + this._hovered = ImGui.IsMouseHoveringRect(pos, pos + size); + this._dragging = this._lastFrameWasDragging && ImGui.IsMouseDown(ImGuiMouseButton.Left); + this._locked = (this._unlocked && !this._lastFrameWasUnlocked || !this._hovered) && !this._dragging; + this._lastFrameWasDragging = this._hovered || this._dragging; } public void Draw(Vector2 pos) @@ -123,16 +122,16 @@ public void Draw(Vector2 pos) if (ImGui.IsMouseHoveringRect(localPos, localPos + size)) { - this.ScrollPosition -= (int)ImGui.GetIO().MouseWheel; + this._scrollPosition -= (int)ImGui.GetIO().MouseWheel; } this.UpdateDragData(localPos, size, this.GeneralConfig.Lock); - bool needsInput = this.Unlocked || !this.GeneralConfig.ClickThrough; - DrawHelpers.DrawInWindow($"##{this.ID}", localPos, size, needsInput, this.Locked || this.GeneralConfig.Lock, (drawList) => + bool needsInput = this._unlocked || !this.GeneralConfig.ClickThrough; + DrawHelpers.DrawInWindow($"##{this.ID}", localPos, size, needsInput, this._locked || this.GeneralConfig.Lock, (drawList) => { - if (this.Unlocked) + if (this._unlocked) { - if (this.LastFrameWasDragging) + if (this._lastFrameWasDragging) { localPos = ImGui.GetWindowPos(); this.GeneralConfig.Position = localPos - pos; @@ -154,12 +153,12 @@ public void Draw(Vector2 pos) size -= Vector2.One * this.GeneralConfig.BorderThickness * 2; } - if (this.GeneralConfig.Preview && !this.LastFrameWasPreview) + if (this.GeneralConfig.Preview && !this._lastFrameWasPreview) { - this.PreviewEvent = ACTEvent.GetTestData(); + this._previewEvent = ACTEvent.GetTestData(); } - ACTEvent? actEvent = this.GeneralConfig.Preview ? this.PreviewEvent : ACTClient.GetLastEvent(); + ACTEvent? actEvent = this.GeneralConfig.Preview ? this._previewEvent : ACTClient.GetLastEvent(); localPos = this.HeaderConfig.DrawHeader(localPos, size, actEvent?.Encounter, drawList); size = size.AddY(-this.HeaderConfig.HeaderHeight); @@ -168,13 +167,13 @@ public void Draw(Vector2 pos) this.DrawBars(drawList, localPos, size, actEvent); }); - this.LastFrameWasUnlocked = this.Unlocked; - this.LastFrameWasPreview = this.GeneralConfig.Preview; + this._lastFrameWasUnlocked = this._unlocked; + this._lastFrameWasPreview = this.GeneralConfig.Preview; } private void DrawBars(ImDrawListPtr drawList, Vector2 localPos, Vector2 size, ACTEvent? actEvent) { - if (actEvent is not null && actEvent.Combatants.Any()) + if (actEvent?.Combatants is not null && actEvent.Combatants.Any()) { List sortedCombatants = this.GetSortedCombatants(actEvent, this.GeneralConfig.DataType); @@ -189,8 +188,8 @@ private void DrawBars(ImDrawListPtr drawList, Vector2 localPos, Vector2 size, AC int i = 0; if (sortedCombatants.Count > this.BarConfig.BarCount) { - i = Math.Clamp(this.ScrollPosition, 0, sortedCombatants.Count - this.BarConfig.BarCount); - this.ScrollPosition = i; + i = Math.Clamp(this._scrollPosition, 0, sortedCombatants.Count - this.BarConfig.BarCount); + this._scrollPosition = i; } int maxIndex = Math.Min(i + this.BarConfig.BarCount, sortedCombatants.Count); @@ -209,14 +208,7 @@ private void DrawBars(ImDrawListPtr drawList, Vector2 localPos, Vector2 size, AC ConfigColor barColor; if (this.BarConfig.UseJobColor) { - if (Enum.TryParse(combatant.Job, true, out Job job)) - { - barColor = this.BarColorsConfig.GetColor(job); - } - else - { - barColor = this.BarColorsConfig.UKNColor; - } + barColor = this.BarColorsConfig.GetColor(combatant.Job); } else { @@ -230,11 +222,12 @@ private void DrawBars(ImDrawListPtr drawList, Vector2 localPos, Vector2 size, AC private List GetSortedCombatants(ACTEvent actEvent, MeterDataType dataType) { - if (this.LastSortedTimestamp.HasValue && - this.LastSortedTimestamp.Value == actEvent.Timestamp && + if (actEvent.Combatants is null || + this._lastSortedTimestamp.HasValue && + this._lastSortedTimestamp.Value == actEvent.Timestamp && !this.GeneralConfig.Preview) { - return this.LastSortedCombatants; + return this._lastSortedCombatants; } List sortedCombatants = actEvent.Combatants.Values.ToList(); @@ -260,8 +253,8 @@ private List GetSortedCombatants(ACTEvent actEvent, MeterDataType dat return (int)(yFloat - xFloat); }); - this.LastSortedTimestamp = actEvent.Timestamp; - this.LastSortedCombatants = sortedCombatants; + this._lastSortedTimestamp = actEvent.Timestamp; + this._lastSortedCombatants = sortedCombatants; return sortedCombatants; } } diff --git a/LMeter/PluginManager.cs b/LMeter/PluginManager.cs index 30fa9d3..f19bb09 100644 --- a/LMeter/PluginManager.cs +++ b/LMeter/PluginManager.cs @@ -15,22 +15,16 @@ namespace LMeter { public class PluginManager : ILMeterDisposable { - private ClientState ClientState { get; init; } - - private DalamudPluginInterface PluginInterface { get; init; } - - private CommandManager CommandManager { get; init; } - - private WindowSystem WindowSystem { get; init; } - - private ConfigWindow ConfigRoot { get; init; } - - private LMeterConfig Config { get; init; } - private readonly Vector2 _origin = ImGui.GetMainViewport().Size / 2f; - private readonly Vector2 _configSize = new Vector2(550, 550); + private ClientState _clientState; + private DalamudPluginInterface _pluginInterface; + private CommandManager _commandManager; + private WindowSystem _windowSystem; + private ConfigWindow _configRoot; + private LMeterConfig _config; + private readonly ImGuiWindowFlags _mainWindowFlags = ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoScrollbar | @@ -46,51 +40,51 @@ public PluginManager( DalamudPluginInterface pluginInterface, LMeterConfig config) { - this.ClientState = clientState; - this.CommandManager = commandManager; - this.PluginInterface = pluginInterface; - this.Config = config; - - this.ConfigRoot = new ConfigWindow("ConfigRoot", _origin, _configSize); + this._clientState = clientState; + this._commandManager = commandManager; + this._pluginInterface = pluginInterface; + this._config = config; - this.WindowSystem = new WindowSystem("LMeter"); - this.WindowSystem.AddWindow(this.ConfigRoot); + this._configRoot = new ConfigWindow("ConfigRoot", _origin, _configSize); + this._windowSystem = new WindowSystem("LMeter"); + this._windowSystem.AddWindow(this._configRoot); - this.CommandManager.AddHandler( + this._commandManager.AddHandler( "/lm", new CommandInfo(PluginCommand) { HelpMessage = "Opens the LMeter configuration window.\n" + "/lm end → Ends current ACT Encounter.\n" + "/lm clear → Clears all ACT encounter log data.\n" + + "/lm ct → Toggles clickthrough status for the given profile.\n" + "/lm toggle → Toggles visibility for the given profile.", ShowInHelp = true } ); - this.ClientState.Logout += OnLogout; - this.PluginInterface.UiBuilder.OpenConfigUi += OpenConfigUi; - this.PluginInterface.UiBuilder.Draw += Draw; + this._clientState.Logout += OnLogout; + this._pluginInterface.UiBuilder.OpenConfigUi += OpenConfigUi; + this._pluginInterface.UiBuilder.Draw += Draw; } private void Draw() { - if (this.ClientState.LocalPlayer == null || CharacterState.IsCharacterBusy()) + if (this._clientState.LocalPlayer == null || CharacterState.IsCharacterBusy()) { return; } - this.WindowSystem.Draw(); + this._windowSystem.Draw(); - this.Config.ACTConfig.TryReconnect(); - this.Config.ACTConfig.TryEndEncounter(); + this._config.ACTConfig.TryReconnect(); + this._config.ACTConfig.TryEndEncounter(); ImGuiHelpers.ForceNextWindowMainViewport(); ImGui.SetNextWindowPos(Vector2.Zero); ImGui.SetNextWindowSize(ImGui.GetMainViewport().Size); if (ImGui.Begin("LMeter_Root", this._mainWindowFlags)) { - foreach (var meter in this.Config.MeterList.Meters) + foreach (var meter in this._config.MeterList.Meters) { meter.Draw(_origin); } @@ -102,7 +96,7 @@ private void Draw() public void Clear(bool clearAct = false) { ACTClient.Clear(clearAct); - foreach (var meter in this.Config.MeterList.Meters) + foreach (var meter in this._config.MeterList.Meters) { meter.Clear(); } @@ -110,14 +104,14 @@ public void Clear(bool clearAct = false) public void Edit(IConfigurable configItem) { - this.ConfigRoot.PushConfig(configItem); + this._configRoot.PushConfig(configItem); } private void OpenConfigUi() { - if (!this.ConfigRoot.IsOpen) + if (!this._configRoot.IsOpen) { - this.ConfigRoot.PushConfig(this.Config); + this._configRoot.PushConfig(this._config); } } @@ -128,18 +122,20 @@ private void OnLogout(object? sender, EventArgs? args) private void PluginCommand(string command, string arguments) { + switch (arguments) { case "end": ACTClient.EndEncounter(); break; case "clear": - this.Clear(this.Config.ACTConfig.ClearACT); + this.Clear(this._config.ACTConfig.ClearACT); break; case { } argument when argument.StartsWith("toggle"): - string[] args = argument.Split(" "); - if (args.Length > 1 && int.TryParse(args[1], out int num)) - this.ToggleMeter(num - 1); + this._config.MeterList.ToggleMeter(GetIntArg(argument) - 1); + break; + case { } argument when argument.StartsWith("ct"): + this._config.MeterList.ToggleClickThrough(GetIntArg(argument) - 1); break; default: this.ToggleWindow(); @@ -147,20 +143,21 @@ private void PluginCommand(string command, string arguments) } } - private void ToggleMeter(int meterIndex) + private static int GetIntArg(string argument) { - this.Config.MeterList.ToggleMeter(meterIndex); + string[] args1 = argument.Split(" "); + return args1.Length > 1 && int.TryParse(args1[1], out int num) ? num : 0; } private void ToggleWindow() { - if (this.ConfigRoot.IsOpen) + if (this._configRoot.IsOpen) { - this.ConfigRoot.IsOpen = false; + this._configRoot.IsOpen = false; } else { - this.ConfigRoot.PushConfig(this.Config); + this._configRoot.PushConfig(this._config); } } @@ -175,11 +172,11 @@ protected virtual void Dispose(bool disposing) if (disposing) { // Don't modify order - this.PluginInterface.UiBuilder.Draw -= Draw; - this.PluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUi; - this.ClientState.Logout -= OnLogout; - this.CommandManager.RemoveHandler("/lm"); - this.WindowSystem.RemoveAllWindows(); + this._pluginInterface.UiBuilder.Draw -= Draw; + this._pluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUi; + this._clientState.Logout -= OnLogout; + this._commandManager.RemoveHandler("/lm"); + this._windowSystem.RemoveAllWindows(); } } } diff --git a/LMeter/changelog.md b/LMeter/changelog.md index 3369d2d..a91fb7a 100644 --- a/LMeter/changelog.md +++ b/LMeter/changelog.md @@ -1,11 +1,13 @@ # Version 0.1.4.0 - Added advanced text-tag formatting (kilo-format and decimal-format) -- Added text command to show/hide Meters (/lm toggle) +- Text Format fields have been reset to default (please check out the new text tags!) +- Added text command to show/hide Meters (/lm toggle ) +- Added text command to show/hide Meters (/lm ct ) - Added option to hide Meter if ACT is not connected - Added option to automatically attempt to reconnect to ACT - Added option to add gaps between bars - Added "Combat" job group to Visibility settings -- Fixed various bugs +- Fixed various bugs and improved performance # Version 0.1.3.1 - Make auto-end disabled by default From 3d7efc86b58fbf4e56666e3fe0c6bcf69fc5383b Mon Sep 17 00:00:00 2001 From: Lichie Date: Wed, 15 Dec 2021 00:29:03 -0800 Subject: [PATCH 4/5] remove usings --- LMeter/Config/BarConfig.cs | 1 - LMeter/Plugin.cs | 4 ++-- LMeter/Windows/ConfigWindow.cs | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/LMeter/Config/BarConfig.cs b/LMeter/Config/BarConfig.cs index fcc7fb5..b23aa35 100644 --- a/LMeter/Config/BarConfig.cs +++ b/LMeter/Config/BarConfig.cs @@ -1,4 +1,3 @@ -using System; using System.Numerics; using ImGuiNET; using LMeter.Helpers; diff --git a/LMeter/Plugin.cs b/LMeter/Plugin.cs index 97dd760..5c68090 100644 --- a/LMeter/Plugin.cs +++ b/LMeter/Plugin.cs @@ -11,13 +11,13 @@ using Dalamud.Game.Command; using Dalamud.Game.Gui; using Dalamud.Interface; +using Dalamud.Logging; using Dalamud.Plugin; using ImGuiScene; +using LMeter.ACT; using LMeter.Config; using LMeter.Helpers; using SigScanner = Dalamud.Game.SigScanner; -using Dalamud.Logging; -using LMeter.ACT; namespace LMeter { diff --git a/LMeter/Windows/ConfigWindow.cs b/LMeter/Windows/ConfigWindow.cs index 914bb1e..b9c2cc0 100644 --- a/LMeter/Windows/ConfigWindow.cs +++ b/LMeter/Windows/ConfigWindow.cs @@ -6,7 +6,6 @@ using ImGuiNET; using LMeter.Config; using LMeter.Helpers; -using LMeter.Meter; namespace LMeter.Windows { From 8de021f69fe8e1ef4a355c0738e9772e9e4dc661 Mon Sep 17 00:00:00 2001 From: Lichie Date: Wed, 15 Dec 2021 00:33:59 -0800 Subject: [PATCH 5/5] update to 0.1.4.0 --- LMeter/LMeter.csproj | 6 +++--- LMeter/Plugin.cs | 2 +- Properties/AssemblyInfo.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/LMeter/LMeter.csproj b/LMeter/LMeter.csproj index 9c2dea9..5c12a39 100644 --- a/LMeter/LMeter.csproj +++ b/LMeter/LMeter.csproj @@ -10,9 +10,9 @@ LMeter - 0.1.3.1 - 0.1.3.1 - 0.1.3.1 + 0.1.4.0 + 0.1.4.0 + 0.1.4.0 diff --git a/LMeter/Plugin.cs b/LMeter/Plugin.cs index 5c68090..8b0050c 100644 --- a/LMeter/Plugin.cs +++ b/LMeter/Plugin.cs @@ -25,7 +25,7 @@ public class Plugin : IDalamudPlugin { public const string ConfigFileName = "LMeter.json"; - public static string Version { get; private set; } = "0.1.3.1"; + public static string Version { get; private set; } = "0.1.4.0"; public static string ConfigFileDir { get; private set; } = ""; diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index ed66b3a..0f9011a 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.3.1")] -[assembly: AssemblyFileVersion("0.1.3.1")] \ No newline at end of file +[assembly: AssemblyVersion("0.1.4.0")] +[assembly: AssemblyFileVersion("0.1.4.0")] \ No newline at end of file