diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 66dcb75..5f80d3a 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -43,20 +43,12 @@ jobs: - name: Build Plugin run: | - invoke-expression 'dotnet build --no-restore --configuration Release RotationSolver' + invoke-expression 'dotnet build --no-restore --configuration Release ActionTimelineEx' - name: Upload Artifact uses: actions/upload-artifact@v3 with: - path: .\RotationSolver\bin\Release\net7.0-windows\RotationSolver\ - - - name: publish on version change - id: publish_nuget - uses: alirezanet/publish-nuget@v3.0.4 - with: - PROJECT_FILE_PATH: RotationSolver.Basic/RotationSolver.Basic.csproj - VERSION_FILE_PATH: Directory.Build.props - NUGET_KEY: ${{secrets.nuget_api_key}} + path: .\ActionTimelineEx\bin\Release\net7.0-windows\ActionTimelineEx\ release: name: release diff --git a/ActionTimelineEx/Configurations/DrawingSettings.cs b/ActionTimelineEx/Configurations/DrawingSettings.cs index 72e0c3b..51940e2 100644 --- a/ActionTimelineEx/Configurations/DrawingSettings.cs +++ b/ActionTimelineEx/Configurations/DrawingSettings.cs @@ -41,6 +41,9 @@ public class DrawingSettings public Vector4 BackgroundColor = new Vector4(0.5f, 0.5f, 0.5f, 0.5f); public Vector4 GCDBorderColor = new Vector4(0.9f, 0.9f, 0.9f, 1f); public float GCDThickness = 1.5f; + public float GCDHeightLow = 0.5f; + public float GCDHeightHigh = 0.8f; + public float GCDRound = 2; public Vector4 CastInProgressColor = new Vector4(0.2f, 0.8f, 0.2f, 1f); public Vector4 CastFinishedColor = new Vector4(0.5f, 0.5f, 0.5f, 1f); @@ -50,7 +53,7 @@ public class DrawingSettings public Vector4 AnimationLockColor = new Vector4(0.8f, 0.7f, 0.6f, 1f); public bool ShowStatusLine = true; - public float StatusLineSize = 24; + public float StatusLineSize = 18; public bool ShowGrid = true; public bool ShowGridCenterLine = false; @@ -59,9 +62,11 @@ public class DrawingSettings public bool GridSubdivideSeconds = true; public int GridSubdivisionCount = 2; public float GridLineWidth = 1; + public float GridCenterLineWidth = 0.1f; public float GridStartLineWidth = 3; public float GridSubdivisionLineWidth = 1; public Vector4 GridLineColor = new Vector4(0.3f, 0.3f, 0.3f, 1f); + public Vector4 GridCenterLineColor = new Vector4(0.5f, 0.5f, 0.5f, 0.3f); public Vector4 GridStartLineColor = new Vector4(0.3f, 0.5f, 0.2f, 1f); public Vector4 GridSubdivisionLineColor = new Vector4(0.3f, 0.3f, 0.3f, 0.2f); diff --git a/ActionTimelineEx/Configurations/Settings.cs b/ActionTimelineEx/Configurations/Settings.cs index 91e7751..fe39bc3 100644 --- a/ActionTimelineEx/Configurations/Settings.cs +++ b/ActionTimelineEx/Configurations/Settings.cs @@ -9,9 +9,9 @@ public class Settings : IPluginConfiguration { public bool ShowTimelineOnlyInDuty = false; public bool ShowTimelineOnlyInCombat = false; - public float StatusCheckDelay = 0.1f; + //public float StatusCheckDelay = 0.1f; public DrawingSettings TimelineSetting = new DrawingSettings(); - + public HashSet HideStatusIds = new HashSet(); public int Version { get; set; } = 6; public void Save() diff --git a/ActionTimelineEx/Plugin.cs b/ActionTimelineEx/Plugin.cs index 6e1d010..ec96b6e 100644 --- a/ActionTimelineEx/Plugin.cs +++ b/ActionTimelineEx/Plugin.cs @@ -19,6 +19,8 @@ public class Plugin : IDalamudPlugin { 10155, 1 }, //PLD Fight or Flight. + { 12556, 1 }, //WAR Inner Strength. + { 17926, 1 }, //DRK Blood Weapon. { 13601, 1 }, //GNB No mercy. @@ -93,9 +95,33 @@ public void Dispose() } [Cmd("/atl", "Opens the ActionTimeline configuration window.")] + [SubCmd("lock", "Lock all windows")] + [SubCmd("unlock", "Unlock all windows")] private void PluginCommand(string command, string arguments) { - _settingsWindow.IsOpen = !_settingsWindow.IsOpen; + var sub = arguments.Split(' ').FirstOrDefault(); + if(string.Equals("unlock", sub, StringComparison.OrdinalIgnoreCase)) + { + foreach (var window in _windowSystem.Windows) + { + if (window is not TimelineWindow tWindow) continue; + + tWindow.Setting.Locked = false; + } + } + else if (string.Equals("lock", sub, StringComparison.OrdinalIgnoreCase)) + { + foreach (var window in _windowSystem.Windows) + { + if (window is not TimelineWindow tWindow) continue; + + tWindow.Setting.Locked = true; + } + } + else + { + _settingsWindow.IsOpen = !_settingsWindow.IsOpen; + } } private void CreateWindows() diff --git a/ActionTimelineEx/Timeline/ActionControlCategory.cs b/ActionTimelineEx/Timeline/ActionControlCategory.cs new file mode 100644 index 0000000..6009950 --- /dev/null +++ b/ActionTimelineEx/Timeline/ActionControlCategory.cs @@ -0,0 +1,21 @@ +namespace ActionTimelineEx.Timeline; + +/// +/// From https://github.com/Kouzukii/ffxiv-deathrecap/blob/master/Game/ActorControlCategory.cs +/// +public enum ActorControlCategory : ushort +{ + Death = 0x6, + CancelAbility = 0xF, + GainEffect = 0x14, + LoseEffect = 0x15, + UpdateEffect = 0x16, + TargetIcon = 0x22, + Tether = 0x23, + Targetable = 0x36, + DirectorUpdate = 0x6D, + SetTargetSign = 0x1F6, + LimitBreak = 0x1F9, + HoT = 0x603, + DoT = 0x604 +} diff --git a/ActionTimelineEx/Timeline/StatusLineItem.cs b/ActionTimelineEx/Timeline/StatusLineItem.cs index b1bd2e5..c033f2e 100644 --- a/ActionTimelineEx/Timeline/StatusLineItem.cs +++ b/ActionTimelineEx/Timeline/StatusLineItem.cs @@ -34,7 +34,7 @@ public void DrawItemWithCenter(Vector2 centerPos, Vector2 windowPos, DrawingSett var statusHeight = setting.StatusLineSize; var flag = ImDrawFlags.RoundCornersAll; - var rounding = 2; + var rounding = setting.GCDRound; TextureWrap? texture = DrawHelper.GetTextureFromIconId(Icon); if (texture == null) return; diff --git a/ActionTimelineEx/Timeline/TimelineItem.cs b/ActionTimelineEx/Timeline/TimelineItem.cs index 20cf81f..7e21b15 100644 --- a/ActionTimelineEx/Timeline/TimelineItem.cs +++ b/ActionTimelineEx/Timeline/TimelineItem.cs @@ -127,9 +127,9 @@ private void DrawItemWithCenter(ImDrawListPtr drawList, Vector2 centerPos, Vecto case TimelineLayer.General: //Get Info. - float highPos = 0.5f; - float lowPos = 0.8f; - float rounding = 2f; + float highPos = setting.GCDHeightLow; + float lowPos = setting.GCDHeightHigh; + float rounding = setting.GCDRound; var leftTop = new Vector2(centerPos.X, centerPos.Y - iconSize / 2 + highPos * iconSize); var leftBottom = new Vector2(centerPos.X, centerPos.Y - iconSize / 2 + lowPos * iconSize); diff --git a/ActionTimelineEx/Timeline/TimelineManager.cs b/ActionTimelineEx/Timeline/TimelineManager.cs index c6a0d50..d164861 100644 --- a/ActionTimelineEx/Timeline/TimelineManager.cs +++ b/ActionTimelineEx/Timeline/TimelineManager.cs @@ -9,6 +9,7 @@ using ECommons.Hooks; using ECommons.Hooks.ActionEffectTypes; using FFXIVClientStructs.FFXIV.Client.Game; +using Lumina.Excel.GeneratedSheets; using RotationSolver.Basic.Data; using Action = Lumina.Excel.GeneratedSheets.Action; using Status = Lumina.Excel.GeneratedSheets.Status; @@ -70,7 +71,7 @@ protected void Dispose(bool disposing) } #endregion - private delegate void OnActorControlDelegate(uint entityId, uint id, uint unk1, uint type, uint unk2, uint unk3, uint unk4, uint unk5, ulong targetId, byte unk6); + private delegate void OnActorControlDelegate(uint entityId, ActorControlCategory type, uint buffID, uint direct, uint actionId, uint sourceId, uint arg4, uint arg5, ulong targetId, byte a10); [Signature("E8 ?? ?? ?? ?? 0F B7 0B 83 E9 64", DetourName = nameof(OnActorControl))] private readonly Hook? _onActorControlHook = null; @@ -78,6 +79,7 @@ protected void Dispose(bool disposing) [Signature("40 55 56 48 81 EC ?? ?? ?? ?? 48 8B EA", DetourName = nameof(OnCast))] private readonly Hook? _onCastHook = null; + public static SortedSet ShowedStatusId { get; } = new SortedSet(); public DateTime EndTime { get; private set; } = DateTime.Now; private static int kMaxItemCount = 128; @@ -91,7 +93,7 @@ private void AddItem(TimelineItem item) _items.Dequeue(); } _items.Enqueue(item); - _lastItem = item; + if(item.Type != TimelineItemType.AutoAttack) _lastItem = item; UpdateEndTime(item.EndTime); } @@ -170,7 +172,8 @@ private static TimelineItemType GetActionType(uint actionId, ActionType type) : TimelineItemType.GCD; case ActionType.Item: - return TimelineItemType.OGCD; + var item = Svc.Data.GetExcelSheet()?.GetRow(actionId); + return item?.CastTimes > 0 ? TimelineItemType.GCD : TimelineItemType.OGCD; } return TimelineItemType.GCD; @@ -186,12 +189,36 @@ private void CancelCasting() _lastItem.CastingTime = MathF.Min(maxTime, _lastItem.CastingTime); } - private async void ActionFromSelfAsync(ActionEffectSet set) + private uint GetStatusIcon(ushort id, bool isGain, byte stack = byte.MaxValue) + { + if (Plugin.Settings.HideStatusIds.Contains(id)) return 0; + var status = Svc.Data.GetExcelSheet()?.GetRow(id); + if (status == null) return 0; + + ShowedStatusId.Add(id); + var icon = status.Icon; + + if (isGain) + { + return icon + (uint)Math.Max(0, status.MaxStacks - 1); + } + else + { + if(stack == byte.MaxValue) + { + stack = Player.Object.StatusList.FirstOrDefault(s => s.StatusId == id)?.StackCount ?? 0; + stack++; + } + return icon + (uint)Math.Max(0, stack - 1); + } + } + + private void ActionFromSelfAsync(ActionEffectSet set) { if (!Player.Available) return; #if DEBUG - //Svc.Chat.Print($"Id: {set.Header.ActionID}; {set.Header.ActionType}"); + //Svc.Chat.Print($"Id: {set.Header.ActionID}; {set.Header.ActionType}; Source: {set.Source.ObjectId}"); #endif if (set.Source.ObjectId != Player.Object.ObjectId) return; @@ -209,24 +236,19 @@ private async void ActionFromSelfAsync(ActionEffectSet set) var isTargetMe = set.TargetEffects[0].TargetID == Player.Object.ObjectId; set.TargetEffects[0].ForEach(x => { - var status = Svc.Data.GetExcelSheet()?.GetRow(x.value); - if (status == null) return; - - var icon = status.Icon; - switch (x.type) { case ActionEffectType.ApplyStatusEffectTarget: case ActionEffectType.ApplyStatusEffectSource when isTargetMe: case ActionEffectType.GpGain: - statusGain.Add(icon + (uint)Math.Max(0, status.MaxStacks - 1)); + var icon = GetStatusIcon(x.value, true); + if (icon != 0) statusGain.Add(icon); break; case ActionEffectType.LoseStatusEffectTarget: case ActionEffectType.LoseStatusEffectSource when isTargetMe: - var stack = Player.Object.StatusList.FirstOrDefault(s => s.StatusId == x.value)?.StackCount ?? 0; - stack++; - statusLose.Add(icon + (uint)Math.Max(0, stack - 1)); + icon = GetStatusIcon(x.value, false); + if (icon != 0) statusLose.Add(icon); break; } }); @@ -273,98 +295,139 @@ private async void ActionFromSelfAsync(ActionEffectSet set) if (effectItem?.Type is TimelineItemType.AutoAttack) return; - int statusDelay = 0; - if(Plugin.Settings.StatusCheckDelay is > 0 and < 0.5f) - { - statusDelay = (int)(Plugin.Settings.StatusCheckDelay * 1000); - var previousStatus = Player.Object.StatusList - .Where(s => s.SourceId == Player.Object.ObjectId && (s.RemainingTime > Plugin.Settings.StatusCheckDelay || s.RemainingTime <= 0)) - .Select(s => (s.StatusId, s.StackCount)) - .ToArray(); + //int statusDelay = 0; + //if (Plugin.Settings.StatusCheckDelay is > 0 and < 0.5f) + //{ + // statusDelay = (int)(Plugin.Settings.StatusCheckDelay * 1000); + // var previousStatus = Player.Object.StatusList + // .Where(s => s.SourceId == Player.Object.ObjectId && (s.RemainingTime > Plugin.Settings.StatusCheckDelay || s.RemainingTime <= 0)) + // .Select(s => (s.StatusId, s.StackCount)) + // .ToArray(); + + // await Task.Delay(statusDelay); + + // var nowStatus = Player.Object.StatusList + // .Where(s => s.SourceId == Player.Object.ObjectId) + // .Select(s => (s.StatusId, s.StackCount)) + // .ToArray(); + + // foreach (var pre in previousStatus) + // { + // var status = Svc.Data.GetExcelSheet()?.GetRow(pre.StatusId); + // if (status == null) continue; + // var now = nowStatus.FirstOrDefault(i => i.StatusId == pre.StatusId); + // if (now.StatusId == 0 || now.StackCount < pre.StackCount) + // { + // effectItem?.StatusLoseIcon.Add(status.Icon + (uint)Math.Max(0, pre.StackCount - 1)); + // } + // } + + // foreach (var now in nowStatus) + // { + // var status = Svc.Data.GetExcelSheet()?.GetRow(now.StatusId); + // if (status == null) continue; + // var pre = previousStatus.FirstOrDefault(i => i.StatusId == now.StatusId); + // if (pre.StatusId == 0) + // { + // effectItem?.StatusGainIcon.Add(status.Icon + (uint)Math.Max(0, now.StackCount - 1)); + // } + // } + //} + + AddStatusLine(effectItem, set.TargetEffects[0].TargetID); + } - await Task.Delay(statusDelay); + private async void AddStatusLine(TimelineItem? effectItem, ulong targetId) + { + if (effectItem == null) return; - var nowStatus = Player.Object.StatusList - .Where(s => s.SourceId == Player.Object.ObjectId) - .Select(s => (s.StatusId, s.StackCount)) - .ToArray(); + await Task.Delay(100); - foreach (var pre in previousStatus) - { - var status = Svc.Data.GetExcelSheet()?.GetRow(pre.StatusId); - if (status == null) continue; - var now = nowStatus.FirstOrDefault(i => i.StatusId == pre.StatusId); - if (now.StatusId == 0 || now.StackCount < pre.StackCount) - { - effectItem?.StatusLoseIcon.Add(status.Icon + (uint)Math.Max(0, pre.StackCount - 1)); - } - } + if (!effectItem.StatusGainIcon.Any()) return; - foreach (var now in nowStatus) + List list = new List(4); + foreach (var icon in effectItem.StatusGainIcon) + { + if (Plugin.IconStack.TryGetValue(icon, out var stack)) { - var status = Svc.Data.GetExcelSheet()?.GetRow(now.StatusId); - if (status == null) continue; - var pre = previousStatus.FirstOrDefault(i => i.StatusId == now.StatusId); - if (pre.StatusId == 0) + var item = new StatusLineItem() { - effectItem?.StatusGainIcon.Add(status.Icon + (uint)Math.Max(0, now.StackCount - 1)); - } + Icon = icon, + TimeDuration = 6, + Stack = stack, + StartTime = effectItem.StartTime, + }; + list.Add(item); + AddItem(item); } } - if (effectItem != null) + var statusList = Player.Object.StatusList.Where(s => s.SourceId == Player.Object.ObjectId); + if (Svc.Objects.SearchById(targetId) is BattleChara b) { - List list = new List(4); - foreach (var icon in effectItem.StatusGainIcon) - { - if (Plugin.IconStack.TryGetValue(icon, out var stack)) - { - var item = new StatusLineItem() - { - Icon = icon, - TimeDuration = 6, - Stack = stack, - StartTime = effectItem.StartTime, - }; - list.Add(item); - AddItem(item); - } - } + statusList = statusList.Union(b.StatusList.Where(s => s.SourceId == Player.Object.ObjectId)); + } - var statusList = Player.Object.StatusList.Where(s => s.SourceId == Player.Object.ObjectId); - if ( Svc.Objects.SearchById(set.TargetEffects[0].TargetID) is BattleChara b) - { - statusList = statusList.Union(b.StatusList.Where(s => s.SourceId == Player.Object.ObjectId)); - } + await Task.Delay(900); - await Task.Delay(1000 - statusDelay); + foreach (var status in statusList) + { + var icon = Svc.Data.GetExcelSheet()?.GetRow(status.StatusId)?.Icon; + if (icon == null) continue; - foreach (var status in statusList) + foreach (var item in list) { - var icon = Svc.Data.GetExcelSheet()?.GetRow(status.StatusId)?.Icon; - if (icon == null) continue; - - foreach (var item in list) + if (item.Icon == icon) { - if (item.Icon == icon) - { - item.TimeDuration = (float)(DateTime.Now - effectItem.StartTime).TotalSeconds + status.RemainingTime; - } + item.TimeDuration = (float)(DateTime.Now - effectItem.StartTime).TotalSeconds + status.RemainingTime; } } } } - private void OnActorControl(uint entityId, uint type, uint buffID, uint direct, uint actionId, uint sourceId, uint arg4, uint arg5, ulong targetId, byte a10) + private async void OnActorControl(uint entityId, ActorControlCategory type, uint buffID, uint direct, uint actionId, uint sourceId, uint arg4, uint arg5, ulong targetId, byte a10) { + var stack = Player.Object.StatusList.FirstOrDefault(s => s.StatusId == buffID && s.SourceId == Player.Object.ObjectId)?.StackCount ?? 0; + _onActorControlHook?.Original(entityId, type, buffID, direct, actionId, sourceId, arg4, arg5, targetId, a10); - if (type != 15) { return; } +//#if DEBUG +// if (type is ActorControlCategory.UpdateEffect) +// { +// Svc.Chat.Print($"Type: {type}, Buff: {buffID}, Direct: {direct}, Action: {actionId}, Source: {sourceId}, Arg4: {arg4}, Arg5: {arg5}, Target: {targetId}, a10: {a10}"); +// } +//#endif - PlayerCharacter? player = Player.Object; - if (player == null || entityId != player.ObjectId) { return; } + if (entityId == Player.Object.ObjectId) + { + switch (type) + { + case ActorControlCategory.CancelAbility: + CancelCasting(); + break; + + case ActorControlCategory.LoseEffect: + await Task.Delay(50); + + var icon = GetStatusIcon((ushort)buffID, false, stack); + if (icon != 0) _lastItem?.StatusLoseIcon.Add(icon); + break; + + //case ActorControlCategory.UpdateEffect: + // await Task.Delay(50); - CancelCasting(); + // icon = GetStatusIcon((ushort)direct, false, (byte)actionId); + // if (icon != 0) _lastItem?.StatusLoseIcon.Add(icon); + // break; + + case ActorControlCategory.GainEffect: + await Task.Delay(50); + + icon = GetStatusIcon((ushort)buffID, true); + if (icon != 0) _lastItem?.StatusGainIcon.Add(icon); + break; + } + } } private unsafe void OnCast(uint sourceId, IntPtr ptr) @@ -379,7 +442,7 @@ private unsafe void OnCast(uint sourceId, IntPtr ptr) var action = Svc.Data.GetExcelSheet()?.GetRow(actionId); var icon = actionId == 4 ? (ushort)118 //Mount - : action?.Icon ?? 0; + : action?.Icon ?? 0; AddItem(new TimelineItem() { diff --git a/ActionTimelineEx/Windows/SettingsWindow.cs b/ActionTimelineEx/Windows/SettingsWindow.cs index 9ab84a7..e1b8b5f 100644 --- a/ActionTimelineEx/Windows/SettingsWindow.cs +++ b/ActionTimelineEx/Windows/SettingsWindow.cs @@ -1,9 +1,12 @@ using ActionTimeline.Helpers; +using ActionTimeline.Timeline; using ActionTimelineEx.Configurations; using Dalamud.Interface; using Dalamud.Interface.Windowing; +using ECommons.Commands; +using ECommons.DalamudServices; using ImGuiNET; -using System.Drawing; +using Lumina.Excel.GeneratedSheets; using System.Numerics; namespace ActionTimeline.Windows @@ -40,21 +43,109 @@ public override void Draw() DrawTimelineSetting(Settings.TimelineSetting); ImGui.EndTabItem(); } + if (ImGui.BeginTabItem("Help")) + { + CmdManager.DrawHelp(); + ImGui.EndTabItem(); + } ImGui.EndTabBar(); } + private ushort _aboutAdd = 0; private void DrawGeneralSetting() { ImGui.Checkbox("Show Only In Duty", ref Settings.ShowTimelineOnlyInDuty); ImGui.Checkbox("Show Only In Combat", ref Settings.ShowTimelineOnlyInCombat); + //ImGui.NewLine(); + + //ImGui.DragFloat("Status checking delay (seconds)", ref Settings.StatusCheckDelay, 0.01f, 0, 1); + + ImGui.NewLine(); + + var index = 0; + + if(ImGui.CollapsingHeader("Showed Statuses")) + { + foreach (var statusId in TimelineManager.ShowedStatusId) + { + var status = Svc.Data.GetExcelSheet()?.GetRow(statusId); + var texture = DrawHelper.GetTextureFromIconId(status?.Icon ?? 0); + if (texture != null) + { + ImGui.Image(texture.ImGuiHandle, new Vector2(18, 24)); + var tips = $"{status?.Name ?? string.Empty} [{status?.RowId ?? 0}]"; + DrawHelper.SetTooltip(tips); + if (++index % 10 != 0) ImGui.SameLine(); + } + } + } + + ImGui.SameLine(); ImGui.NewLine(); - ImGui.DragFloat("Status checking delay (seconds)", ref Settings.StatusCheckDelay, 0.01f, 0, 1); + ImGui.Text("Don't show these status."); + + if (ImGui.BeginChild("ExceptStatus", new Vector2(0f, -1f), true)) + { + ushort removeId = 0, addId = 0; + index = 0; + foreach (var statusId in Plugin.Settings.HideStatusIds) + { + var status = Svc.Data.GetExcelSheet()?.GetRow(statusId); + var texture = DrawHelper.GetTextureFromIconId(status?.Icon ?? 0); + if (texture != null) + { + ImGui.Image(texture.ImGuiHandle, new Vector2(24, 30)); + DrawHelper.SetTooltip(status?.Name ?? string.Empty); + ImGui.SameLine(); + } + + var id = statusId.ToString(); + ImGui.SetNextItemWidth(100 * _scale); + if (ImGui.InputText($"##Status{index++}", ref id, 8) && ushort.TryParse(id, out var newId)) + { + removeId = statusId; + addId = newId; + } + + ImGui.SameLine(); + + ImGui.PushFont(UiBuilder.IconFont); + if (ImGui.Button($"{FontAwesomeIcon.Ban.ToIconString()}##Remove{statusId}")) + { + removeId = statusId; + } + ImGui.PopFont(); + } + var oneId = string.Empty; + ImGui.SetNextItemWidth(100 * _scale); + if (ImGui.InputText($"##AddOne", ref oneId, 8) && ushort.TryParse(oneId, out var newOneId)) + { + _aboutAdd = newOneId; + } + ImGui.SameLine(); + + ImGui.PushFont(UiBuilder.IconFont); + if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconString()}##AddNew")) + { + addId = _aboutAdd; + } + ImGui.PopFont(); + + if (removeId != 0) + { + Plugin.Settings.HideStatusIds.Remove(removeId); + } + if (addId != 0) + { + Plugin.Settings.HideStatusIds.Add(addId); + } + ImGui.EndChild(); + } } #region Timeline - private void DrawTimelineSetting(DrawingSettings settings) { if (!ImGui.BeginTabBar("##Timeline_Settings_TabBar")) @@ -124,8 +215,6 @@ private void DrawGeneralTab(DrawingSettings settings) DrawHelper.SetTooltip("This is the advanced time about action using"); } ImGui.DragFloat("Drawing Center offset", ref settings.CenterOffset, 0.3f, -500, 500); - - } private void DrawIconsTab(DrawingSettings settings) @@ -184,7 +273,8 @@ private void DrawBarTab(DrawingSettings settings) ImGui.ColorEdit4("Bar Background Color", ref settings.BackgroundColor, ImGuiColorEditFlags.NoInputs); ImGui.ColorEdit4("GCD Border Color", ref settings.GCDBorderColor, ImGuiColorEditFlags.NoInputs); ImGui.DragFloat("GCD Border Thickness", ref settings.GCDThickness, 0.01f, 0, 10); - + ImGui.DragFloat("GCD Border Round", ref settings.GCDRound, 0.01f, 0, 10); + ImGui.DragFloatRange2("GCD Bar Height", ref settings.GCDHeightLow, ref settings.GCDHeightHigh, 0.01f, 0, 1); ImGui.NewLine(); ImGui.ColorEdit4("Cast In Progress Color", ref settings.CastInProgressColor, ImGuiColorEditFlags.NoInputs); @@ -218,14 +308,24 @@ private void DrawGridTab(DrawingSettings settings) { ImGui.Checkbox("Enabled", ref settings.ShowGrid); - ImGui.DragFloat("Start Line Width", ref settings.GridStartLineWidth, 0.5f, 1, 5); + ImGui.DragFloat("Start Line Width", ref settings.GridStartLineWidth, 0.1f, 0.1f, 10); ImGui.ColorEdit4("Start Line Color", ref settings.GridStartLineColor, ImGuiColorEditFlags.NoInputs); - if (!settings.ShowGrid) { return; } + ImGui.NewLine(); ImGui.Checkbox("Show Center Line", ref settings.ShowGridCenterLine); - ImGui.DragFloat("Line Width", ref settings.GridLineWidth, 0.5f, 1, 5); + if (settings.ShowGridCenterLine) + { + ImGui.Indent(); + ImGui.DragFloat("Center Line Width", ref settings.GridCenterLineWidth, 0.1f, 0.1f, 10); + ImGui.ColorEdit4("Center Line Color", ref settings.GridCenterLineColor, ImGuiColorEditFlags.NoInputs); + ImGui.Unindent(); + } + + ImGui.NewLine(); + + ImGui.DragFloat("Line Width", ref settings.GridLineWidth, 0.1f, 0.1f, 10); ImGui.ColorEdit4("Line Color", ref settings.GridLineColor, ImGuiColorEditFlags.NoInputs); ImGui.NewLine(); @@ -240,7 +340,7 @@ private void DrawGridTab(DrawingSettings settings) if (!settings.GridSubdivideSeconds) { return; } - ImGui.DragInt("Sub-Division Count", ref settings.GridSubdivisionCount, 0.5f, 2, 8); + ImGui.DragInt("Sub-Division Count", ref settings.GridSubdivisionCount, 0.2f, 2, 8); ImGui.DragFloat("Sub-Division Line Width", ref settings.GridSubdivisionLineWidth, 0.5f, 1, 5); ImGui.ColorEdit4("Sub-Division Line Color", ref settings.GridSubdivisionLineColor, ImGuiColorEditFlags.NoInputs); } diff --git a/ActionTimelineEx/Windows/TimelineWindow.cs b/ActionTimelineEx/Windows/TimelineWindow.cs index 123368f..2e111b6 100644 --- a/ActionTimelineEx/Windows/TimelineWindow.cs +++ b/ActionTimelineEx/Windows/TimelineWindow.cs @@ -139,40 +139,41 @@ private void DrawGrid(Vector2 pos, Vector2 size) uint lineColor = ImGui.ColorConvertFloat4ToU32(Setting.GridLineColor); uint subdivisionLineColor = ImGui.ColorConvertFloat4ToU32(Setting.GridSubdivisionLineColor); - if (Setting.ShowGridCenterLine) + if (Setting.GridDivideBySeconds) { - drawList.AddLine(new Vector2(pos.X, pos.Y + height / 2f), new Vector2(pos.X + width, pos.Y + height / 2f), lineColor, Setting.GridLineWidth); - } - - if (!Setting.GridDivideBySeconds) return; + float step = Setting.SizePerSecond; - float step = Setting.SizePerSecond; + for (int i = 0; i < width / step; i++) + { + float x = step * i; + var start = pos.X + width - x; - for (int i = 0; i < width / step; i++) - { - float x = step * i; - var start = pos.X + width - x; + if (Setting.GridSubdivideSeconds && Setting.GridSubdivisionCount > 1) + { + float subStep = step * 1f / Setting.GridSubdivisionCount; + for (int j = 1; j < Setting.GridSubdivisionCount; j++) + { + drawList.AddLine(new Vector2(start + subStep * j, pos.Y), new Vector2(start + subStep * j, pos.Y + height), subdivisionLineColor, Setting.GridSubdivisionLineWidth); + } + } + var time = -i + Setting.TimeOffset; - if (Setting.GridSubdivideSeconds && Setting.GridSubdivisionCount > 1) - { - float subStep = step * 1f / Setting.GridSubdivisionCount; - for (int j = 1; j < Setting.GridSubdivisionCount; j++) + if (time != 0) { - drawList.AddLine(new Vector2(start + subStep * j, pos.Y), new Vector2(start + subStep * j, pos.Y + height), subdivisionLineColor, Setting.GridSubdivisionLineWidth); + drawList.AddLine(new Vector2(start, pos.Y), new Vector2(start, pos.Y + height), lineColor, Setting.GridLineWidth); } - } - var time = -i + Setting.TimeOffset; - if (time != 0) - { - drawList.AddLine(new Vector2(start, pos.Y), new Vector2(start, pos.Y + height), lineColor, Setting.GridLineWidth); + if (Setting.GridShowSecondsText) + { + drawList.AddText(new Vector2(start + 2, pos.Y), lineColor, $" {time}s"); + } } + } - if (Setting.GridShowSecondsText) - { - drawList.AddText(new Vector2(start + 2, pos.Y), lineColor, $" {time}s"); - } + lineColor = ImGui.ColorConvertFloat4ToU32(Setting.GridCenterLineColor); + if (Setting.ShowGridCenterLine) + { + drawList.AddLine(new Vector2(pos.X, pos.Y + height / 2f + Setting.CenterOffset), new Vector2(pos.X + width, pos.Y + height / 2f + Setting.CenterOffset), lineColor, Setting.GridCenterLineWidth); } - return; } } diff --git a/ECommons b/ECommons index a59971b..c6bff47 160000 --- a/ECommons +++ b/ECommons @@ -1 +1 @@ -Subproject commit a59971b3db59b173c3069e3509a93f034c4b0832 +Subproject commit c6bff47e22413a2e619849d72257413bcebac640 diff --git a/Images/example.gif b/Images/example.gif index c7c3cda..9fd98e1 100644 Binary files a/Images/example.gif and b/Images/example.gif differ diff --git a/README.md b/README.md index 2d69b01..a36211e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,20 @@ # Action Timeline Plugin -A Final Fantasy XIV Dalamud Plugin that shows a timeline with the actions you use in real-time. +![Github Latest Releases](https://img.shields.io/github/downloads/ArchiDog1998/ActionTimelineEx/latest/total.svg?style=for-the-badge) +![Github All Releases](https://img.shields.io/github/downloads/ArchiDog1998/ActionTimelineEx/total.svg?style=for-the-badge) +![Github Lines](https://img.shields.io/tokei/lines/github/ArchiDog1998/ActionTimelineEx?style=for-the-badge) +![Github License](https://img.shields.io/github/license/ArchiDog1998/ActionTimelineEx.svg?label=License&style=for-the-badge) +![Github Commits](https://img.shields.io/github/commits-since/ArchiDog1998/ActionTimelineEx/latest/main?style=for-the-badge) -![example](https://github.com/Tischel/ActionTimeline/blob/master/Images/example.gif) +A Final Fantasy XIV Dalamud Plugin that shows a timeline with the actions you use in real-time. It was forked from [Tischel](https://github.com/Tischel/ActionTimeline). + +![example](/Images/example.gif) + +Download it at this url: + +``` +https://raw.githubusercontent.com/ArchiDog1998/Dalamud_Plugins/main/pluginmaster.json +``` ## Disclaimer