diff --git a/.gitmodules b/.gitmodules index 7cabb88..d75eb40 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "XIVConfigUI"] path = XIVConfigUI url = https://github.com/ArchiDog1998/XIVConfigUI +[submodule "XIVDrawer"] + path = XIVDrawer + url = https://github.com/ArchiDog1998/XIVDrawer diff --git a/ActionTimelineEx.sln b/ActionTimelineEx.sln index e30723a..38b0088 100644 --- a/ActionTimelineEx.sln +++ b/ActionTimelineEx.sln @@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActionTimelineEx", "ActionT EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XIVConfigUI", "XIVConfigUI\XIVConfigUI\XIVConfigUI.csproj", "{E3AA7E1F-8B6C-4B5C-BE87-1B54BC5003CC}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XIVDrawer", "XIVDrawer\XIVDrawer\XIVDrawer.csproj", "{A269BD49-DF07-445F-AF13-3BAC46D4F2CD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -46,6 +48,14 @@ Global {E3AA7E1F-8B6C-4B5C-BE87-1B54BC5003CC}.Release|Any CPU.Build.0 = Release|Any CPU {E3AA7E1F-8B6C-4B5C-BE87-1B54BC5003CC}.Release|x64.ActiveCfg = Release|Any CPU {E3AA7E1F-8B6C-4B5C-BE87-1B54BC5003CC}.Release|x64.Build.0 = Release|Any CPU + {A269BD49-DF07-445F-AF13-3BAC46D4F2CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A269BD49-DF07-445F-AF13-3BAC46D4F2CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A269BD49-DF07-445F-AF13-3BAC46D4F2CD}.Debug|x64.ActiveCfg = Debug|Any CPU + {A269BD49-DF07-445F-AF13-3BAC46D4F2CD}.Debug|x64.Build.0 = Debug|Any CPU + {A269BD49-DF07-445F-AF13-3BAC46D4F2CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A269BD49-DF07-445F-AF13-3BAC46D4F2CD}.Release|Any CPU.Build.0 = Release|Any CPU + {A269BD49-DF07-445F-AF13-3BAC46D4F2CD}.Release|x64.ActiveCfg = Release|Any CPU + {A269BD49-DF07-445F-AF13-3BAC46D4F2CD}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ActionTimelineEx/ActionTimelineEx.csproj b/ActionTimelineEx/ActionTimelineEx.csproj index e5cc9b9..a2a804a 100644 --- a/ActionTimelineEx/ActionTimelineEx.csproj +++ b/ActionTimelineEx/ActionTimelineEx.csproj @@ -16,7 +16,8 @@ - + + $(DalamudLibPath)Dalamud.dll False diff --git a/ActionTimelineEx/Configurations/ActionSetting.cs b/ActionTimelineEx/Configurations/ActionSetting.cs new file mode 100644 index 0000000..305d007 --- /dev/null +++ b/ActionTimelineEx/Configurations/ActionSetting.cs @@ -0,0 +1,124 @@ +using ActionTimeline.Helpers; +using Dalamud.Plugin.Services; +using ECommons.DalamudServices; +using FFXIVClientStructs.FFXIV.Common.Lua; +using ImGuiNET; +using System.Drawing; +using System.Numerics; +using System.Xml.Linq; +using XIVConfigUI.Attributes; + +namespace ActionTimelineEx.Configurations; + +internal class ActionSettingAttribute() : ListUIAttribute(0) +{ + public override uint GetIcon(object obj) + { + if (obj is not ActionSetting setting) + return base.GetIcon(obj); + return setting.IconId; + } + + public override void OnClick(object obj) + { + base.OnClick(obj); + if (obj is not ActionSetting setting) return; + + //TODO: Change the acion ID... + } +} + +public enum ActionSettingType : byte +{ + Action, + Item, +} + +[ActionSetting] +public class ActionSetting +{ + internal uint IconId { get; private set; } = 0; + internal bool IsGCD { get; private set; } = false; + + [UI("Name")] + internal string DisplayName { get; private set; } = ""; + + private uint _actionId; + + [UI("Id")] + public uint ActionId + { + get => _actionId; + set + { + if (value == _actionId) return; + _actionId = value; + + Update(); + } + } + + private ActionSettingType _type; + + [UI("Type")] + public ActionSettingType Type + { + get => _type; + set + { + if (value == _type) return; + _type = value; + + Update(); + } + } + + private void Update() + { + ClearData(); + + switch (Type) + { + case ActionSettingType.Action: + UpdateAction(); + return; + + case ActionSettingType.Item: + UpdateItem(); + return; + } + + void UpdateItem() + { + var item = Svc.Data.GetExcelSheet()?.GetRow(ActionId); + if (item == null) return; + + IconId = item.Icon; + DisplayName = item.Name; + } + + void UpdateAction() + { + var action = Svc.Data.GetExcelSheet()?.GetRow(ActionId); + if (action == null) return; + + IsGCD = action.CooldownGroup == 58 || action.AdditionalCooldownGroup == 58; + + IconId = action.Icon; + DisplayName = $"{action.Name} ({(IsGCD ? "GCD" : "Ability")})"; + } + + void ClearData() + { + IconId = 0; + DisplayName = string.Empty; + IsGCD = false; + } + } + + public void Draw(ImDrawListPtr drawList, Vector2 point, float size) + { + drawList.DrawActionIcon(IconId, Type is ActionSettingType.Item, point, size); + if (!string.IsNullOrEmpty(DisplayName) && DrawHelper.IsInRect(point, new Vector2(size))) ImGui.SetTooltip(DisplayName); + } +} diff --git a/ActionTimelineEx/Configurations/RotationSetting.cs b/ActionTimelineEx/Configurations/RotationSetting.cs new file mode 100644 index 0000000..f6fe448 --- /dev/null +++ b/ActionTimelineEx/Configurations/RotationSetting.cs @@ -0,0 +1,10 @@ +using XIVConfigUI.Attributes; + +namespace ActionTimelineEx.Configurations; +public class RotationSetting +{ + [UI("Rotation Name")] + public string Name { get; set; } = "Default"; + + public List Actions { get; set; } = []; +} diff --git a/ActionTimelineEx/Configurations/RotationsSetting.cs b/ActionTimelineEx/Configurations/RotationsSetting.cs new file mode 100644 index 0000000..8aab047 --- /dev/null +++ b/ActionTimelineEx/Configurations/RotationsSetting.cs @@ -0,0 +1,29 @@ +using System.Text.Json.Serialization; + +namespace ActionTimelineEx.Configurations; + +public class RotationsSetting +{ + public string Choice { get; set; } = "Default"; + public List RotationSettings { get; set; } = []; + + [JsonIgnore] + public RotationSetting RotationSetting + { + get + { + var result = RotationSettings.FirstOrDefault(r => r.Name == Choice); + if (result != null) return result; + + result = RotationSettings.FirstOrDefault(); + if (result == null) + { + result = new(); + RotationSettings.Add(result); + } + + Choice = result.Name; + return result; + } + } +} diff --git a/ActionTimelineEx/Configurations/Settings.cs b/ActionTimelineEx/Configurations/Settings.cs index a27114c..9114285 100644 --- a/ActionTimelineEx/Configurations/Settings.cs +++ b/ActionTimelineEx/Configurations/Settings.cs @@ -1,11 +1,22 @@ using ActionTimelineEx.Configurations; using Dalamud.Configuration; using ECommons.DalamudServices; +using ECommons.ExcelServices; +using ECommons.GameHelpers; +using Newtonsoft.Json; using System.Numerics; using XIVConfigUI.Attributes; namespace ActionTimeline; +internal class TimelineChoicesAttribute : ChoicesAttribute +{ + protected override Pair[] GetChoices() + { + return [..Plugin.Settings.EditSetting?.RotationSettings.Select(i => i.Name)]; + } +} + public class Settings : IPluginConfiguration { [UI("Record Data")] @@ -35,9 +46,77 @@ public class Settings : IPluginConfiguration [UI("Show the donate link.")] public bool ShowDonate { get; set; } = true; - public List TimelineSettings = []; + public List TimelineSettings { get; set; } = []; public HashSet HideStatusIds { get; set; } = []; + [UI("Draw Rotation", 1)] + public bool DrawRotation { get; set; } = false; + + [JsonIgnore] + [TimelineChoices] + [UI("Rotation Choice", Parent = nameof(DrawRotation))] + public string RotationChoice + { + get => EditSetting?.Choice ?? "Default"; + set + { + if (EditSetting == null) return; + EditSetting.Choice = value; + } + } + + [JsonIgnore] + [UI("Rotation Name", Parent = nameof(DrawRotation))] + public string RotationName + { + get => EditSetting?.RotationSetting.Name ?? "Default"; + set + { + if (EditSetting == null) return; + EditSetting.RotationSetting.Name = value; + } + } + + [UI("Locked", Parent = nameof(DrawRotation))] + public bool RotationLocked { get; set; } = false; + + [UI("Locked Background Color", Parent = nameof(DrawRotation))] + public Vector4 RotationLockedBackgroundColor { get; set; } = new(0f, 0f, 0f, 0.5f); + + [UI("Unlocked Background Color", Parent = nameof(DrawRotation))] + public Vector4 RotationUnlockedBackgroundColor { get; set; } = new(0f, 0f, 0f, 0.75f); + + [UI("Rotation Highlight Color", Parent = nameof(DrawRotation))] + public Vector4 RotationHighlightColor { get; set; } = new Vector4(1, 1, 1, 1); + + [Range(1, 100, ConfigUnitType.Pixels, 0.2f)] + [UI("GCD Icon Size", Parent = nameof(DrawRotation))] + public int GCDIconSize { get; set; } = 40; + + [Range(1, 100, ConfigUnitType.Pixels, 0.2f)] + [UI("Off GCD Icon Size", Parent = nameof(DrawRotation))] + public int OGCDIconSize { get; set; } = 30; + + [Range(1, 100, ConfigUnitType.Pixels, 0.2f)] + [UI("Icon Spacing", Parent = nameof(DrawRotation))] + public int IconSpacing { get; set; } = 5; + + [JsonProperty] + private Dictionary> _rotationHelpers = []; + + [JsonIgnore] + internal RotationsSetting? EditSetting { get; set; } = null; + + public RotationsSetting GetSetting(uint territoryId) + { + if (!_rotationHelpers.TryGetValue(territoryId, out var dict)) _rotationHelpers[territoryId] = dict = []; + + var job = Player.Job; + if (!dict.TryGetValue(job, out var result)) dict[job] = result = new(); + + return result; + } + public int Version { get; set; } = 6; public void Save() diff --git a/ActionTimelineEx/Configurations/UiString.cs b/ActionTimelineEx/Configurations/UiString.cs index 32c8d72..eb15e55 100644 --- a/ActionTimelineEx/Configurations/UiString.cs +++ b/ActionTimelineEx/Configurations/UiString.cs @@ -25,4 +25,10 @@ internal enum UiString [Description("Remove this Item:")] RemoveDesc, + + [Description("Rotation Setting")] + RotationSetting, + + [Description("Rotation")] + Rotation, } diff --git a/ActionTimelineEx/Helpers/DrawHelper.cs b/ActionTimelineEx/Helpers/DrawHelper.cs index ab5c34a..f822cec 100644 --- a/ActionTimelineEx/Helpers/DrawHelper.cs +++ b/ActionTimelineEx/Helpers/DrawHelper.cs @@ -1,4 +1,5 @@ -using Dalamud.Interface.Textures; +using ActionTimelineEx.Configurations; +using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.TextureWraps; using ECommons.DalamudServices; using ImGuiNET; @@ -27,12 +28,6 @@ public static void DrawActionIcon(this ImDrawListPtr drawList, uint iconId, bool } } - public static Vector4 ChangeAlpha(this Vector4 color, float alpha) - { - color.Z = alpha; - return color; - } - public static IDalamudTextureWrap? GetTextureFromIconId(uint iconId, bool highQuality = true) => ImageLoader.GetTexture(new GameIconLookup( iconId, false, highQuality), out var texture) ? texture : ImageLoader.GetTexture(new GameIconLookup(0, false, highQuality), out texture) ? texture : null; diff --git a/ActionTimelineEx/Helpers/RotationHelper.cs b/ActionTimelineEx/Helpers/RotationHelper.cs new file mode 100644 index 0000000..8c228f3 --- /dev/null +++ b/ActionTimelineEx/Helpers/RotationHelper.cs @@ -0,0 +1,126 @@ +using ActionTimeline; +using ActionTimelineEx.Configurations; +using ActionTimelineEx.Windows; +using ECommons.DalamudServices; +using ECommons.GameHelpers; +using ECommons.Hooks; +using ECommons.Hooks.ActionEffectTypes; +using Lumina.Excel.GeneratedSheets; +using XIVDrawer.ElementSpecial; + +namespace ActionTimelineEx.Helpers; + +internal static class RotationHelper +{ + private static DrawingHighlightHotbar? _highLight; + public static ActionSetting? ActiveActon => Actions.FirstOrDefault(); + + public static IEnumerable< ActionSetting> Actions => RotationSetting.Actions.Skip((int)Count); + + public static RotationSetting RotationSetting => Plugin.Settings.GetSetting(Svc.ClientState.TerritoryType).RotationSetting; + + private static uint _count; + public static uint Count + { + get => _count; + private set + { + if (_count == value) return; + _count = value; + + if (_highLight == null) return; + _highLight.Color = Plugin.Settings.RotationHighlightColor; + _highLight.HotbarIDs.Clear(); + + var action = ActiveActon; + if (action == null) return; + + HotbarID? hotbar = null; + + switch (action.Type) + { + case ActionSettingType.Action: + hotbar = new HotbarID(FFXIVClientStructs.FFXIV.Client.UI.Misc.RaptureHotbarModule.HotbarSlotType.Action, action.ActionId); + break; + + case ActionSettingType.Item: + hotbar = new HotbarID(FFXIVClientStructs.FFXIV.Client.UI.Misc.RaptureHotbarModule.HotbarSlotType.Item, action.ActionId); + break; + } + + if (hotbar == null) return; + _highLight.HotbarIDs.Add(hotbar.Value); + } + } + public static uint SuccessCount { get; private set; } = 0; + + public static void Init() + { + ActionEffect.ActionEffectEvent += ActionFromSelf; + Svc.DutyState.DutyWiped += DutyState_DutyWiped; + Svc.DutyState.DutyCompleted += DutyState_DutyWiped; + Svc.ClientState.TerritoryChanged += ClientState_TerritoryChanged; + + _highLight = new(); + } + + public static void Dispose() + { + ActionEffect.ActionEffectEvent -= ActionFromSelf; + Svc.DutyState.DutyWiped -= DutyState_DutyWiped; + Svc.DutyState.DutyCompleted -= DutyState_DutyWiped; + Svc.ClientState.TerritoryChanged -= ClientState_TerritoryChanged; + } + + private static void ClientState_TerritoryChanged(ushort obj) + { + Clear(); + + var territory = Svc.Data.GetExcelSheet()?.GetRow(obj); + if (IsTerritoryTypeValid(territory)) + { + RotationHelperItem._territoryId = obj; + } + } + + private static void DutyState_DutyWiped(object? sender, ushort e) + { + Clear(); + } + + private static void ActionFromSelf(ActionEffectSet set) + { + if (!Player.Available) return; + if (set.Source.EntityId != Player.Object.EntityId || !Plugin.Settings.DrawRotation) return; + if (set.Action == null) return; + + var action = ActiveActon; + if (action == null) return; + + var succeed = set.Action.RowId == action.ActionId; + if (succeed) + { + SuccessCount++; + } + else + { + Svc.Chat.Print("Failed to click the action!"); + } + Count++; + } + + public static void Clear() + { + Count = 0; + } + + public static bool IsTerritoryTypeValid(TerritoryType? territory) + { + if (territory == null) return false; + if (territory.BattalionMode == 0) return false; + if (territory.IsPvpZone) return false; + if (territory.PlaceName.Row == 0) return false; + + return true; + } +} diff --git a/ActionTimelineEx/Localization/Localization.json b/ActionTimelineEx/Localization/Localization.json index 51536af..18bde58 100644 --- a/ActionTimelineEx/Localization/Localization.json +++ b/ActionTimelineEx/Localization/Localization.json @@ -105,5 +105,12 @@ "XIVConfigUI.Attributes.ConfigUnitType.Percent": "Ratio Unit, as percentage.", "XIVConfigUI.Attributes.ConfigUnitType.Pixels": "Display Unit, in pixels.", "ActionTimelineEx.Punchline": "Show your actions in real-time.", - "ActionTimelineEx.Description": "Configurable timeline display of all the actions you use." + "ActionTimelineEx.Description": "Configurable timeline display of all the actions you use.", + "ActionTimelineEx.Windows.ChangeLogItem": "ChangeLog", + "ActionTimeline.SettingsName.DrawRotation": "Draw Rotation", + "ActionTimeline.SettingsName.RotationChoice": "Rotation Choice", + "ActionTimeline.SettingsName.RotationName": "Rotation Name", + "ActionTimeline.SettingsName.RotationCount": "Rotation Count", + "ActionTimeline.SettingsName.RotationHighlightColor": "Rotation Highlight Color", + "ActionTimeline.SettingsName.Position": "Rotation Position" } \ No newline at end of file diff --git a/ActionTimelineEx/Plugin.cs b/ActionTimelineEx/Plugin.cs index 5585505..72dbe85 100644 --- a/ActionTimelineEx/Plugin.cs +++ b/ActionTimelineEx/Plugin.cs @@ -1,7 +1,8 @@ -using ActionTimeline.Helpers; -using ActionTimeline.Timeline; +using ActionTimeline.Timeline; using ActionTimeline.Windows; using ActionTimelineEx.Configurations; +using ActionTimelineEx.Helpers; +using ActionTimelineEx.Windows; using Dalamud.Game.ClientState.Conditions; using Dalamud.Interface.Windowing; using Dalamud.Plugin; @@ -9,6 +10,7 @@ using ECommons.DalamudServices; using ECommons.GameHelpers; using XIVConfigUI; +using XIVDrawer; namespace ActionTimeline; @@ -67,12 +69,14 @@ public Plugin(IDalamudPluginInterface pluginInterface) { ECommonsMain.Init(pluginInterface, this); XIVConfigUIMain.Init(pluginInterface, "/atle", "Opens the ActionTimelineEx configuration window.", PluginCommand, typeof(Settings), typeof(DrawingSettings), typeof(GroupItem), typeof(UiString)); + XIVDrawerMain.Init(pluginInterface, "ActionTimelineExOverlay"); Svc.PluginInterface.UiBuilder.Draw += Draw; Svc.PluginInterface.UiBuilder.OpenConfigUi += OpenConfigUi; Svc.PluginInterface.UiBuilder.OpenMainUi += OpenConfigUi; TimelineManager.Initialize(); + RotationHelper.Init(); try { @@ -91,10 +95,12 @@ public void Dispose() Settings.Save(); TimelineManager.Instance?.Dispose(); + RotationHelper.Dispose(); _windowSystem.RemoveAllWindows(); XIVConfigUIMain.Dispose(); + XIVDrawerMain.Dispose(); ECommonsMain.Dispose(); Svc.PluginInterface.UiBuilder.Draw -= Draw; @@ -130,6 +136,7 @@ private void Draw() { TimelineWindow.Draw(setting, index++); } + RotationHelperWindow.Draw(); } private static bool ShowTimeline() diff --git a/ActionTimelineEx/Timeline/TimelineItem.cs b/ActionTimelineEx/Timeline/TimelineItem.cs index 5daee09..ecb3dc7 100644 --- a/ActionTimelineEx/Timeline/TimelineItem.cs +++ b/ActionTimelineEx/Timeline/TimelineItem.cs @@ -91,7 +91,7 @@ private void DrawItemWithCenter(ImDrawListPtr drawList, Vector2 centerPos, Vecto case TimelineLayer.Icon: var pos = centerPos - iconSize / 2 * setting.RealDownDirection; drawList.DrawActionIcon(Icon, IsHq, pos, iconSize); - if (!string.IsNullOrEmpty(Name) && DrawHelper.IsInRect(pos, new Vector2( iconSize))) ImGui.SetTooltip(Name); + if (!string.IsNullOrEmpty(Name) && DrawHelper.IsInRect(pos, new Vector2(iconSize))) ImGui.SetTooltip(Name); return; diff --git a/ActionTimelineEx/Timeline/TimelineManager.cs b/ActionTimelineEx/Timeline/TimelineManager.cs index a35d442..30cd83c 100644 --- a/ActionTimelineEx/Timeline/TimelineManager.cs +++ b/ActionTimelineEx/Timeline/TimelineManager.cs @@ -161,6 +161,9 @@ private static TimelineItemType GetActionType(uint actionId, ActionType type) case ActionType.Item: var item = Svc.Data.GetExcelSheet()?.GetRow(actionId); return item?.CastTimes > 0 ? TimelineItemType.GCD : TimelineItemType.OGCD; + + case ActionType.Mount: + return TimelineItemType.GCD; } return TimelineItemType.OGCD; @@ -268,7 +271,7 @@ private void ActionFromSelf(ActionEffectSet set) && _lastItem.State == TimelineItemState.Casting) // Finish the casting. { _lastItem.AnimationLockTime = set.Header.AnimationLockTime; - _lastItem.Name = set.Name; + _lastItem.Name = $"{set.Name} ({set.Header.ActionID})"; _lastItem.Icon = set.IconId; _lastItem.Damage = damage; _lastItem.State = TimelineItemState.Finished; @@ -281,7 +284,7 @@ private void ActionFromSelf(ActionEffectSet set) AnimationLockTime = type == TimelineItemType.AutoAttack ? 0 : set.Header.AnimationLockTime, GCDTime = type == TimelineItemType.GCD ? GCD : 0, Type = type, - Name = set.Name, + Name = $"{set.Name} ({set.Header.ActionID})", Icon = set.IconId, Damage = damage, State = TimelineItemState.Finished, diff --git a/ActionTimelineEx/Windows/RotationHelperItem.cs b/ActionTimelineEx/Windows/RotationHelperItem.cs new file mode 100644 index 0000000..66075c0 --- /dev/null +++ b/ActionTimelineEx/Windows/RotationHelperItem.cs @@ -0,0 +1,101 @@ +using ActionTimeline; +using ActionTimelineEx.Configurations; +using ActionTimelineEx.Helpers; +using Dalamud.Interface.Colors; +using Dalamud.Interface.Textures.TextureWraps; +using Dalamud.Interface.Utility.Raii; +using ECommons.DalamudServices; +using ImGuiNET; +using Lumina.Excel.GeneratedSheets; +using System.ComponentModel; +using System.Numerics; +using XIVConfigUI; +using XIVConfigUI.ConditionConfigs; +using XIVDrawer; + +namespace ActionTimelineEx.Windows; + +[Description("Rotation Helper")] +internal class RotationHelperItem() : ConfigWindowItem +{ + internal static uint _territoryId { get; set; } = 0; + + private static CollapsingHeaderGroup? _group; + + private static TerritoryType[]? _territories; + + public override bool GetIcon(out IDalamudTextureWrap texture) + { + return ImageLoader.GetTexture(25, out texture); + } + + public override void Draw(ConfigWindow window) + { + var setting = Plugin.Settings.GetSetting(_territoryId); + Plugin.Settings.EditSetting = setting; + + DrawTerritoryHeader(); + + _group ??= new CollapsingHeaderGroup(new() + { + { () => UiString.RotationSetting.Local(), () => window.Collection.DrawItems(1) }, + { () => UiString.Rotation.Local(), () => ConditionDrawer.Draw(setting.RotationSetting.Actions) }, + }); + + _group.Draw(); + base.Draw(window); + } + + private static void DrawTerritoryHeader() + { + _territories ??= Svc.Data.GetExcelSheet()? + .Where(RotationHelper.IsTerritoryTypeValid) + .Reverse().ToArray(); + + var rightTerritory = Svc.Data.GetExcelSheet()?.GetRow(_territoryId); + var name = GetName(rightTerritory); + + var imFont = DrawingExtensions.GetFont(21); + float width = 0; + using (var font = ImRaii.PushFont(imFont)) + { + width = ImGui.CalcTextSize(name).X + (ImGui.GetStyle().ItemSpacing.X * 2); + } + + ImGuiHelper.DrawItemMiddle(() => + { + var territories = _territories ?? []; + var index = Array.IndexOf(territories, rightTerritory); + if (ImGuiHelper.SelectableCombo("##Choice the specific dungeon", [.. territories.Select(GetName)], ref index, imFont, ImGuiColors.DalamudYellow)) + { + _territoryId = territories[index]?.RowId ?? 0; + } + }, ImGui.GetWindowWidth(), width); + + DrawContentFinder(rightTerritory?.ContentFinderCondition?.Value); + + static string GetName(TerritoryType? territory) + { + var str = territory?.ContentFinderCondition?.Value?.Name?.RawString; + if (string.IsNullOrEmpty(str)) str = territory?.Name?.RawString; + if (string.IsNullOrEmpty(str)) return "Unnamed Territory"; + return str; + } + } + + private static void DrawContentFinder(ContentFinderCondition? content) + { + var badge = content?.Image; + if (badge != null && badge.Value != 0 + && ImageLoader.GetTexture(badge.Value, out var badgeTexture)) + { + var wholeWidth = ImGui.GetWindowWidth(); + var size = new Vector2(badgeTexture.Width, badgeTexture.Height) * MathF.Min(1, MathF.Min(480, wholeWidth) / badgeTexture.Width); + + ImGuiHelper.DrawItemMiddle(() => + { + ImGui.Image(badgeTexture.ImGuiHandle, size); + }, wholeWidth, size.X); + } + } +} diff --git a/ActionTimelineEx/Windows/RotationHelperWindow.cs b/ActionTimelineEx/Windows/RotationHelperWindow.cs new file mode 100644 index 0000000..b2d71ec --- /dev/null +++ b/ActionTimelineEx/Windows/RotationHelperWindow.cs @@ -0,0 +1,67 @@ +using ActionTimeline; +using ActionTimeline.Windows; +using ActionTimelineEx.Helpers; +using Dalamud.Interface.Utility; +using ImGuiNET; +using System.Numerics; +using XIVConfigUI; + +namespace ActionTimelineEx.Windows; +internal static class RotationHelperWindow +{ + public static void Draw() + { + var setting = Plugin.Settings; + if (!setting.DrawRotation) return; + + var flag = TimelineWindow._baseFlags; + if (setting.RotationLocked) + { + flag |= ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoMouseInputs; + } + + Vector4 bgColor = setting.RotationLocked ? setting.RotationLockedBackgroundColor : setting.RotationUnlockedBackgroundColor; + ImGui.PushStyleColor(ImGuiCol.WindowBg, bgColor); + + ImGui.SetNextWindowSize(new Vector2(560, 100) * ImGuiHelpers.GlobalScale, ImGuiCond.FirstUseEver); + ImGui.SetNextWindowPos(new Vector2(200, 200) * ImGuiHelpers.GlobalScale, ImGuiCond.FirstUseEver); + + ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(0, 0)); + ImGui.PushStyleVar(ImGuiStyleVar.WindowBorderSize, 0); + + if (ImGui.Begin("Rotation Helper Window", flag)) + { + DrawContent(); + ImGui.End(); + } + + ImGui.PopStyleVar(2); + ImGui.PopStyleColor(); + } + + private static void DrawContent() + { + var gcdHeight = Plugin.Settings.GCDIconSize; + var ogcdHeight = Plugin.Settings.OGCDIconSize; + var spacing = Plugin.Settings.IconSpacing; + var drawList = ImGui.GetWindowDrawList(); + + var pos = ImGui.GetWindowPos() + new Vector2(0, ImGui.GetWindowSize().Y / 2 - gcdHeight / 2); + var maxX = pos.X + ImGui.GetWindowSize().X; + + bool isFirst = true; + foreach (var item in RotationHelper.Actions) + { + var size = item.IsGCD ? gcdHeight : ogcdHeight; + item.Draw(drawList, pos, size); + if (isFirst) + { + drawList.DrawSlotHighlight(pos, size, ImGui.ColorConvertFloat4ToU32(Plugin.Settings.RotationHighlightColor)); + isFirst = false; + } + pos += new Vector2(size + spacing, 0); + + if (pos.X >= maxX) break; + } + } +} diff --git a/ActionTimelineEx/Windows/SettingsWindow.cs b/ActionTimelineEx/Windows/SettingsWindow.cs index 438a07a..3de2118 100644 --- a/ActionTimelineEx/Windows/SettingsWindow.cs +++ b/ActionTimelineEx/Windows/SettingsWindow.cs @@ -3,14 +3,11 @@ using ActionTimelineEx.Configurations; using ActionTimelineEx.Windows; using Dalamud.Interface; -using Dalamud.Interface.Colors; using Dalamud.Interface.GameFonts; -using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Utility; using ECommons.DalamudServices; using ImGuiNET; using Lumina.Excel.GeneratedSheets; -using System.ComponentModel; using System.Numerics; using XIVConfigUI; using XIVConfigUI.SearchableConfigs; @@ -23,7 +20,7 @@ public override IEnumerable Searchables { get { - if(ActiveItem is TimelineItem item) + if (ActiveItem is TimelineItem item) { return [..base.Searchables, ..item.Collection]; } @@ -34,80 +31,6 @@ public override IEnumerable Searchables } } - internal class TimelineItem(DrawingSettings setting, System.Action clearItems) : ConfigWindowItem - { - internal readonly SearchableCollection Collection = new(setting); - private CollapsingHeaderGroup? _extraHeader; - - public override string Name => setting.Name; - - public override bool GetIcon(out IDalamudTextureWrap texture) - { - return ImageLoader.GetTexture(42, out texture); - } - - public override void Draw(ConfigWindow window) - { - if (RemoveValue(setting.Name)) - { - Settings.TimelineSettings.Remove(setting); - clearItems(); - } - - _extraHeader ??= new(new() - { - { () =>GroupItem.General.Local(), () => Collection.DrawItems((int)GroupItem.General) }, - { () =>GroupItem.Icons.Local(), () => Collection.DrawItems((int)GroupItem.Icons) }, - { () =>GroupItem.Bar.Local(), () => Collection.DrawItems((int)GroupItem.Bar) }, - { () =>GroupItem.Grid.Local(), () => Collection.DrawItems((int)GroupItem.Grid) }, - { () =>GroupItem.GcdClipping.Local(), () => Collection.DrawItems((int)GroupItem.GcdClipping) }, - }); - _extraHeader?.Draw(); - } - - private string _undoName = string.Empty; - private DateTime _lastTime = DateTime.MinValue; - private bool RemoveValue(string name) - { - bool isLast = name == _undoName && DateTime.Now - _lastTime < TimeSpan.FromSeconds(2); - bool isTime = DateTime.Now - _lastTime > TimeSpan.FromSeconds(0.5); - - bool result = false; - - if (isLast) ImGui.PushStyleColor(ImGuiCol.Text, isTime ? ImGuiColors.HealerGreen : ImGuiColors.DPSRed); - - ImGui.Text(UiString.RemoveDesc.Local()); - ImGui.SameLine(); - - ImGui.PushFont(UiBuilder.IconFont); - if (ImGui.Button($"{(isLast ? FontAwesomeIcon.Check : FontAwesomeIcon.Ban).ToIconString()}##Remove{name}")) - { - if (isLast && isTime) - { - result = true; - _lastTime = DateTime.MinValue; - } - else - { - _lastTime = DateTime.Now; - _undoName = name; - } - } - - ImGui.PopFont(); - - if (ImGui.IsItemHovered()) - { - ImGui.SetTooltip(!isTime ? UiString.Wait.Local() - : isLast ? UiString.Confirm.Local() - : UiString.Remove.Local()); - } - - if (isLast) ImGui.PopStyleColor(); - return result; - } - } - private static float _scale => ImGuiHelpers.GlobalScale; public override SearchableCollection Collection { get; } = new(Settings); protected override bool ShowDonate => Settings.ShowDonate; @@ -165,6 +88,7 @@ protected override ConfigWindowItem[] GetItems() { return [ + new RotationHelperItem(), ..Settings.TimelineSettings.Select(i => new TimelineItem(i, ClearItems)), new ChangeLogItem(), ]; diff --git a/ActionTimelineEx/Windows/TimelineItem.cs b/ActionTimelineEx/Windows/TimelineItem.cs new file mode 100644 index 0000000..392c4de --- /dev/null +++ b/ActionTimelineEx/Windows/TimelineItem.cs @@ -0,0 +1,83 @@ +using ActionTimeline; +using ActionTimelineEx.Configurations; +using Dalamud.Interface; +using Dalamud.Interface.Colors; +using Dalamud.Interface.Textures.TextureWraps; +using ImGuiNET; +using XIVConfigUI; + +namespace ActionTimelineEx.Windows; + +internal class TimelineItem(DrawingSettings setting, Action clearItems) : ConfigWindowItem +{ + internal readonly SearchableCollection Collection = new(setting); + private CollapsingHeaderGroup? _extraHeader; + + public override string Name => setting.Name; + + public override bool GetIcon(out IDalamudTextureWrap texture) + { + return ImageLoader.GetTexture(42, out texture); + } + + public override void Draw(ConfigWindow window) + { + if (RemoveValue(setting.Name)) + { + Plugin.Settings.TimelineSettings.Remove(setting); + clearItems(); + } + + _extraHeader ??= new(new() + { + { () => GroupItem.General.Local(), () => Collection.DrawItems((int)GroupItem.General) }, + { () => GroupItem.Icons.Local(), () => Collection.DrawItems((int)GroupItem.Icons) }, + { () => GroupItem.Bar.Local(), () => Collection.DrawItems((int)GroupItem.Bar) }, + { () => GroupItem.Grid.Local(), () => Collection.DrawItems((int)GroupItem.Grid) }, + { () => GroupItem.GcdClipping.Local(), () => Collection.DrawItems((int)GroupItem.GcdClipping) }, + }); + _extraHeader?.Draw(); + } + + private string _undoName = string.Empty; + private DateTime _lastTime = DateTime.MinValue; + private bool RemoveValue(string name) + { + bool isLast = name == _undoName && DateTime.Now - _lastTime < TimeSpan.FromSeconds(2); + bool isTime = DateTime.Now - _lastTime > TimeSpan.FromSeconds(0.5); + + bool result = false; + + if (isLast) ImGui.PushStyleColor(ImGuiCol.Text, isTime ? ImGuiColors.HealerGreen : ImGuiColors.DPSRed); + + ImGui.Text(UiString.RemoveDesc.Local()); + ImGui.SameLine(); + + ImGui.PushFont(UiBuilder.IconFont); + if (ImGui.Button($"{(isLast ? FontAwesomeIcon.Check : FontAwesomeIcon.Ban).ToIconString()}##Remove{name}")) + { + if (isLast && isTime) + { + result = true; + _lastTime = DateTime.MinValue; + } + else + { + _lastTime = DateTime.Now; + _undoName = name; + } + } + + ImGui.PopFont(); + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(!isTime ? UiString.Wait.Local() + : isLast ? UiString.Confirm.Local() + : UiString.Remove.Local()); + } + + if (isLast) ImGui.PopStyleColor(); + return result; + } +} diff --git a/ActionTimelineEx/Windows/TimelineWindow.cs b/ActionTimelineEx/Windows/TimelineWindow.cs index 39d3e03..f3bd844 100644 --- a/ActionTimelineEx/Windows/TimelineWindow.cs +++ b/ActionTimelineEx/Windows/TimelineWindow.cs @@ -9,7 +9,7 @@ namespace ActionTimeline.Windows; internal static class TimelineWindow { - private const ImGuiWindowFlags _baseFlags = ImGuiWindowFlags.NoScrollbar + internal const ImGuiWindowFlags _baseFlags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoNav @@ -41,7 +41,6 @@ public static void Draw(DrawingSettings setting, int index) } ImGui.PopStyleVar(2); - ImGui.PopStyleColor(); } diff --git a/ActionTimelineEx/packages.lock.json b/ActionTimelineEx/packages.lock.json index a9b9a3b..e64ade3 100644 --- a/ActionTimelineEx/packages.lock.json +++ b/ActionTimelineEx/packages.lock.json @@ -13,6 +13,11 @@ "resolved": "4.2.3", "contentHash": "SyeAfu2wL5247sipJoPUzQfjiwQtfSd8hN4IbgoyVcDx4PP6Dud4znwPRibWQzLtTlUxYYcbf5f4p+EfFC7KtQ==" }, + "LibTessDotNet": { + "type": "Transitive", + "resolved": "1.1.15", + "contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg==" + }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "5.0.0", @@ -51,6 +56,12 @@ "dependencies": { "Svg": "[3.4.7, )" } + }, + "xivdrawer": { + "type": "Project", + "dependencies": { + "LibTessDotNet": "[1.1.15, )" + } } } } diff --git a/XIVConfigUI b/XIVConfigUI index 697d566..0fa3270 160000 --- a/XIVConfigUI +++ b/XIVConfigUI @@ -1 +1 @@ -Subproject commit 697d5669c905a6d7924b2a23e60f3dac0dce1760 +Subproject commit 0fa327049175f55a81dd22b9d316c976197a8a06 diff --git a/XIVDrawer b/XIVDrawer new file mode 160000 index 0000000..ec941f4 --- /dev/null +++ b/XIVDrawer @@ -0,0 +1 @@ +Subproject commit ec941f459494770e680c76ea07169b4f1e891c76