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