diff --git a/CBT/FlyText/Animations/FlyTextAnimation.cs b/CBT/FlyText/Animations/FlyTextAnimation.cs index 9a9dcb2..2659b97 100644 --- a/CBT/FlyText/Animations/FlyTextAnimation.cs +++ b/CBT/FlyText/Animations/FlyTextAnimation.cs @@ -3,6 +3,7 @@ namespace CBT.FlyText.Animations; using System; using System.Numerics; +using CBT.FlyText.Configuration; using CBT.Types; using FFXIVClientStructs; @@ -53,29 +54,35 @@ public enum FlyTextAlignment /// public abstract class FlyTextAnimation { + /// + /// When set, animation properties (Duration, Speed, Reversed, Alignment) are read from this config instead of FlyTextKinds. + /// Used for Player Damage Taken so it can have different animation settings than Dealt. + /// + internal FlyTextConfiguration? EventConfig { get; set; } + /// /// Gets the Animation duration. /// public float Duration - => Service.Configuration.FlyTextKinds[this.FlyTextKind].Animation.Duration; + => (this.EventConfig ?? Service.Configuration.FlyTextKinds[this.FlyTextKind]).Animation.Duration; /// /// Gets the Animation speed. /// public float Speed - => Service.Configuration.FlyTextKinds[this.FlyTextKind].Animation.Speed; + => (this.EventConfig ?? Service.Configuration.FlyTextKinds[this.FlyTextKind]).Animation.Speed; /// /// Gets a value indicating whether the animation direction should be reversed. /// public bool Reversed - => Service.Configuration.FlyTextKinds[this.FlyTextKind].Animation.Reversed; + => (this.EventConfig ?? Service.Configuration.FlyTextKinds[this.FlyTextKind]).Animation.Reversed; /// /// Gets the alignment of an element. /// public FlyTextAlignment Alignment - => Service.Configuration.FlyTextKinds[this.FlyTextKind].Animation.Alignment; + => (this.EventConfig ?? Service.Configuration.FlyTextKinds[this.FlyTextKind]).Animation.Alignment; /// /// Gets or sets the FlyAnimationKind. @@ -126,17 +133,21 @@ public abstract class FlyTextAnimation /// Create an instance of a FlyTextAnimation type. /// /// FlyTextKind determines the animation type. + /// Optional config for this event (e.g. Player Damage Taken). When set, animation reads from this instead of FlyTextKinds. /// FlyTextAnimation implementation. /// Exception thrown when an invalid kind is received. - public static FlyTextAnimation Create(FlyTextKind flyTextKind) + public static FlyTextAnimation Create(FlyTextKind flyTextKind, FlyTextConfiguration? eventConfig = null) { - FlyTextAnimationKind animationKind = Service.Configuration.FlyTextKinds[flyTextKind].Animation.Kind; - return animationKind switch + var config = eventConfig ?? Service.Configuration.FlyTextKinds[flyTextKind]; + FlyTextAnimationKind animationKind = config.Animation.Kind; + FlyTextAnimation animation = animationKind switch { FlyTextAnimationKind.None => new None(flyTextKind), FlyTextAnimationKind.LinearFade => new LinearFade(flyTextKind), _ => throw new ArgumentOutOfRangeException(nameof(flyTextKind), animationKind, null), }; + animation.EventConfig = eventConfig; + return animation; } /// diff --git a/CBT/FlyText/FlyTextReceiver.cs b/CBT/FlyText/FlyTextReceiver.cs index d5f9a9f..005b814 100644 --- a/CBT/FlyText/FlyTextReceiver.cs +++ b/CBT/FlyText/FlyTextReceiver.cs @@ -94,7 +94,7 @@ public unsafe partial class FlyTextReceiver : IDisposable { try { - var kindConfig = PluginManager.GetConfigForKind(kind); + var kindConfig = PluginManager.GetConfigForEvent(kind, source, target); var effects = GetEffects(target->GetActionEffectHandler()); var sourceObjectID = GetGameObjectId(target->GetActionEffectHandler()); diff --git a/CBT/Interface/Tabs/KindTab.cs b/CBT/Interface/Tabs/KindTab.cs index 1f674ac..f6b3ed9 100644 --- a/CBT/Interface/Tabs/KindTab.cs +++ b/CBT/Interface/Tabs/KindTab.cs @@ -3,6 +3,7 @@ namespace CBT.Interface.Tabs; using System.Collections.Generic; using System.Linq; using CBT.Types; +using Dalamud.Bindings.ImGui; /// /// CatgeoryTab configures settings for FlyTextKinds. @@ -30,6 +31,23 @@ public class KindTab : Tab this.DrawCurrentConfigurations(KindPickerValues); + if (this.Current.InCategory(FlyTextCategory.AbilityDamage)) + { + GuiArtist.DrawLabelPrefix("Configure for", sameLine: false); + ImGui.SameLine(); + var isTaken = this.UsePlayerDamageTakenConfig; + if (ImGui.RadioButton("Player Damage Dealt", !isTaken)) + { + this.UsePlayerDamageTakenConfig = false; + } + + ImGui.SameLine(); + if (ImGui.RadioButton("Player Damage Taken", isTaken)) + { + this.UsePlayerDamageTakenConfig = true; + } + } + if (this.CurrentEnabled) { this.DrawFontConfigurations(); diff --git a/CBT/Interface/Tabs/Tab.cs b/CBT/Interface/Tabs/Tab.cs index df03cf0..3527902 100644 --- a/CBT/Interface/Tabs/Tab.cs +++ b/CBT/Interface/Tabs/Tab.cs @@ -27,6 +27,11 @@ public abstract class Tab /// protected abstract FlyTextKind Current { get; set; } + /// + /// When true and current kind is AbilityDamage, configuration edits apply to Player Damage Taken instead of Dealt. + /// + protected bool UsePlayerDamageTakenConfig { get; set; } + /// /// Gets or sets a value indicating whether. /// @@ -235,6 +240,20 @@ public abstract class Tab /// public abstract void OnClose(); + /// + /// Gets the config dictionary to use for the current kind (FlyTextKinds or FlyTextKindsPlayerDamageTaken for AbilityDamage when editing Taken). + /// + protected Dictionary GetCurrentConfigDictionary() + { + if (this.Current.InCategory(FlyTextCategory.AbilityDamage) && this.UsePlayerDamageTakenConfig + && Service.Configuration.FlyTextKindsPlayerDamageTaken != null) + { + return Service.Configuration.FlyTextKindsPlayerDamageTaken; + } + + return Service.Configuration.FlyTextKinds; + } + /// /// Get a configuration value. /// @@ -243,7 +262,8 @@ public abstract class Tab /// The value. protected T GetValue(Func selector) { - return Service.Configuration.FlyTextKinds.TryGetValue(this.Current, out var currentConfig) + var dict = this.GetCurrentConfigDictionary(); + return dict.TryGetValue(this.Current, out var currentConfig) ? selector(currentConfig) : selector(Service.Configuration.FlyTextKinds[this.Current]); } @@ -256,10 +276,13 @@ public abstract class Tab /// The value to set. protected void SetValue(Action setter, T value) { - if (!Service.Configuration.FlyTextKinds.TryGetValue(this.Current, out var currentConfig)) + var dict = this.GetCurrentConfigDictionary(); + if (!dict.TryGetValue(this.Current, out var currentConfig)) { - currentConfig = new FlyTextConfiguration(Service.Configuration.FlyTextKinds[this.Current]); - Service.Configuration.FlyTextKinds[this.Current] = currentConfig; + currentConfig = Service.Configuration.FlyTextKinds.TryGetValue(this.Current, out var baseConfig) + ? new FlyTextConfiguration(baseConfig) + : new FlyTextConfiguration(); + dict[this.Current] = currentConfig; } setter(currentConfig, value); @@ -281,7 +304,7 @@ public abstract class Tab if (this.CurrentEnabled) { - if (Service.Configuration.FlyTextKinds.TryGetValue(this.Current, out var currentConfig)) + if (this.GetCurrentConfigDictionary().TryGetValue(this.Current, out var currentConfig)) { if (this.Current.ShouldAllow(FlyTextFilter.Self)) { diff --git a/CBT/Plugin.cs b/CBT/Plugin.cs index 0d68aa4..d37773f 100644 --- a/CBT/Plugin.cs +++ b/CBT/Plugin.cs @@ -3,6 +3,8 @@ namespace CBT; using System.IO; using System.Reflection; using CBT.FlyText; +using CBT.FlyText.Configuration; +using CBT.Types; using CBT.Helpers; using CBT.Interface; using Dalamud.Game; @@ -54,6 +56,7 @@ public sealed partial class Plugin : IDalamudPlugin ShowInHelp = true, }); Service.Configuration = pluginInterface.GetPluginConfig() as PluginConfiguration ?? new PluginConfiguration(); + MigratePlayerDamageTakenConfig(); Service.Fonts = new FontManager(Path.GetDirectoryName(assemblyLocation) + "\\Media\\Fonts\\"); Service.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfigUi; Service.Interface.UiBuilder.OpenMainUi += this.OnOpenConfigUi; @@ -88,6 +91,29 @@ public sealed partial class Plugin : IDalamudPlugin private void OnOpenConfigUi() => this.configWindow.IsOpen = true; + /// + /// Migrates existing config: ensures FlyTextKindsPlayerDamageTaken is populated for AbilityDamage kinds (e.g. from older saves). + /// + private static void MigratePlayerDamageTakenConfig() + { + var config = Service.Configuration; + if (config.FlyTextKindsPlayerDamageTaken == null || config.FlyTextKindsPlayerDamageTaken.Count == 0) + { + config.FlyTextKindsPlayerDamageTaken = []; + foreach (var kind in FlyTextCategoryExtension.GetKindsFor(FlyTextCategory.AbilityDamage)) + { + if (config.FlyTextKinds.TryGetValue(kind, out var existing)) + { + config.FlyTextKindsPlayerDamageTaken[kind] = new FlyTextConfiguration(existing); + } + else + { + config.FlyTextKindsPlayerDamageTaken[kind] = new FlyTextConfiguration(); + } + } + } + } + /// /// OnCommand reacts to commands received via . /// diff --git a/CBT/PluginConfiguration.cs b/CBT/PluginConfiguration.cs index 4960013..2e194d6 100644 --- a/CBT/PluginConfiguration.cs +++ b/CBT/PluginConfiguration.cs @@ -62,6 +62,12 @@ public class PluginConfiguration : IPluginConfiguration kind => kind, kind => new FlyTextConfiguration()); + this.FlyTextKindsPlayerDamageTaken = FlyTextCategoryExtension + .GetKindsFor(FlyTextCategory.AbilityDamage) + .ToDictionary( + kind => kind, + kind => new FlyTextConfiguration()); + FlyTextCategory.AbilityDamage .ForEachKind(kind => { @@ -71,6 +77,12 @@ public class PluginConfiguration : IPluginConfiguration this.FlyTextKinds[kind].Positionals = true; this.FlyTextKinds[kind].Font.ColorSuccess = new Vector4(0.4f, 1, 0.4f, 1); this.FlyTextKinds[kind].Font.ColorFailed = new Vector4(1, 0.4f, 0.4f, 1); + + this.FlyTextKindsPlayerDamageTaken[kind].Font.Color = new Vector4(1, 1, 0, 1); + this.FlyTextKindsPlayerDamageTaken[kind].Font.Size = 24f; + this.FlyTextKindsPlayerDamageTaken[kind].Positionals = true; + this.FlyTextKindsPlayerDamageTaken[kind].Font.ColorSuccess = new Vector4(0.4f, 1, 0.4f, 1); + this.FlyTextKindsPlayerDamageTaken[kind].Font.ColorFailed = new Vector4(1, 0.4f, 0.4f, 1); }); FlyTextCategory.AutoAttack @@ -146,6 +158,27 @@ public class PluginConfiguration : IPluginConfiguration this.FlyTextKinds[kind].Filter.Self = false; } }); + + FlyTextCategoryExtension + .GetKindsFor(FlyTextCategory.AbilityDamage) + .ToList() + .ForEach(kind => + { + if (kind.ShouldFilter(FlyTextFilter.Party)) + { + this.FlyTextKindsPlayerDamageTaken[kind].Filter.Party = false; + } + + if (kind.ShouldFilter(FlyTextFilter.Enemy)) + { + this.FlyTextKindsPlayerDamageTaken[kind].Filter.Enemy = false; + } + + if (kind.ShouldFilter(FlyTextFilter.Self)) + { + this.FlyTextKindsPlayerDamageTaken[kind].Filter.Self = false; + } + }); } /// @@ -158,6 +191,12 @@ public class PluginConfiguration : IPluginConfiguration /// public Dictionary FlyTextKinds { get; set; } = []; + /// + /// Gets or sets the configuration for ability damage when the player is the target (damage taken). + /// When the player deals damage, is used; when the player takes damage, this is used. + /// + public Dictionary FlyTextKindsPlayerDamageTaken { get; set; } = []; + /// /// Gets or sets the FlyTextCategory Category Configuration options. /// diff --git a/CBT/PluginManager.cs b/CBT/PluginManager.cs index 7f27186..d6edd0d 100644 --- a/CBT/PluginManager.cs +++ b/CBT/PluginManager.cs @@ -103,6 +103,26 @@ public unsafe partial class PluginManager public static FlyTextConfiguration? GetConfigForKind(FlyTextKind kind) => Service.Configuration.FlyTextKinds.TryGetValue(kind, out var config) ? config : null; + /// + /// Get the config for an event. For AbilityDamage kinds, returns Player Damage Taken config when the player is the target, otherwise the normal (Dealt) config. + /// + /// Kind of the event. + /// Source character. + /// Target character. + /// The configuration to use for this event. + public static FlyTextConfiguration? GetConfigForEvent(FlyTextKind kind, Character* source, Character* target) + { + if (kind.InCategory(FlyTextCategory.AbilityDamage) + && target != null + && IsPlayerCharacter(target) + && Service.Configuration.FlyTextKindsPlayerDamageTaken?.TryGetValue(kind, out var takenConfig) == true) + { + return takenConfig; + } + + return GetConfigForKind(kind); + } + /// /// Is the target an enemy. /// diff --git a/CBT/Types/FlyTextEvent.cs b/CBT/Types/FlyTextEvent.cs index cb335ea..72b76a6 100644 --- a/CBT/Types/FlyTextEvent.cs +++ b/CBT/Types/FlyTextEvent.cs @@ -7,6 +7,7 @@ using System.Numerics; using CBT.FlyText.Animations; using CBT.FlyText.Configuration; using CBT.Helpers; +using CBT; using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.TextureWraps; using FFXIVClientStructs.FFXIV.Client.Game.Character; @@ -111,14 +112,7 @@ public unsafe partial class FlyTextEvent { if (this.configuredOffset == null) { - if (Service.Configuration.FlyTextKinds.TryGetValue(this.Kind, out var kindConfig)) - { - this.configuredOffset = kindConfig.Offset; - } - else - { - this.configuredOffset = Vector2.Zero; - } + this.configuredOffset = this.Config?.Offset ?? Vector2.Zero; } return (Vector2)this.configuredOffset; @@ -413,8 +407,9 @@ public unsafe partial class FlyTextEvent // This is a special case for HP Regen attribution. this.SourceID = sourceID; - this.Config = new FlyTextConfiguration(kind); - this.Animation = FlyTextAnimation.Create(kind); + var eventConfig = PluginManager.GetConfigForEvent(kind, source, target); + this.Config = new FlyTextConfiguration(eventConfig ?? Service.Configuration.FlyTextKinds[kind]); + this.Animation = FlyTextAnimation.Create(kind, this.Config); } // TODO @cultbaus: I want to use string formatting and text tags instead of this, but for now this can be a placeholder.