using HSUI.Config; using HSUI.Config.Attributes; using HSUI.Enums; using HSUI.Helpers; using HSUI.Interface.Bars; using HSUI.Interface.GeneralElements; using Dalamud.Bindings.ImGui; using Lumina.Excel; using Lumina.Excel.Sheets; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using LuminaAction = Lumina.Excel.Sheets.Action; namespace HSUI.Interface.PartyCooldowns { public enum PartyCooldownsGrowthDirection { Down = 0, Up, Right, Left } [Exportable(false)] [Section("Party Cooldowns", true)] [SubSection("General", 0)] public class PartyCooldownsConfig : MovablePluginConfigObject { public new static PartyCooldownsConfig DefaultConfig() { var config = new PartyCooldownsConfig(); config.Position = new Vector2(-ImGui.GetMainViewport().Size.X / 2 + 100, -ImGui.GetMainViewport().Size.Y / 2 + 100); return config; } [Checkbox("Preview", isMonitored = true)] [Order(4)] public bool Preview = false; [Combo("Sections Growth Direction", "Down", "Up", "Right", "Left", spacing = true)] [Order(20)] public PartyCooldownsGrowthDirection GrowthDirection = PartyCooldownsGrowthDirection.Down; [DragInt2("Padding", min = -1000, max = 1000)] [Order(15)] public Vector2 Padding = new Vector2(0, -1); [Checkbox("Tooltips", spacing = true)] [Order(16)] public bool ShowTooltips = true; [Checkbox("Show Only in Duties", spacing = true, isMonitored = true)] [Order(20)] public bool ShowOnlyInDuties = true; [Checkbox("Show When Solo", isMonitored = true)] [Order(21)] public bool ShowWhenSolo = false; [NestedConfig("Visibility", 200)] public VisibilityConfig VisibilityConfig = new VisibilityConfig(); } [Disableable(false)] [Exportable(false)] [DisableParentSettings("Position", "Anchor", "HideWhenInactive", "FillColor", "Background", "FillDirection")] [Section("Party Cooldowns", true)] [SubSection("Cooldown Bar", 0)] public class PartyCooldownsBarConfig : BarConfig { [Checkbox("Show Bar", spacing = true)] [Order(70)] public bool ShowBar = true; [ColorEdit4("Available Color")] [Order(71, collapseWith = nameof(ShowBar))] public PluginConfigColor AvailableColor = new PluginConfigColor(new Vector4(0f / 255f, 150f / 255f, 0f / 255f, 100f / 100f)); [ColorEdit4("Available Background Color")] [Order(72, collapseWith = nameof(ShowBar))] public PluginConfigColor AvailableBackgroundColor = new PluginConfigColor(new Vector4(0f / 255f, 150f / 255f, 0f / 255f, 25f / 100f)); [ColorEdit4("Recharging Color")] [Order(73, collapseWith = nameof(ShowBar))] public PluginConfigColor RechargingColor = new PluginConfigColor(new Vector4(150f / 255f, 0f / 255f, 0f / 255f, 100f / 100f)); [ColorEdit4("Recharging Background Color")] [Order(74, collapseWith = nameof(ShowBar))] public PluginConfigColor RechargingBackgroundColor = new PluginConfigColor(new Vector4(150f / 255f, 0f / 255f, 0f / 255f, 25f / 100f)); [Checkbox("Use Job Colors")] [Order(75, collapseWith = nameof(ShowBar))] public bool UseJobColors = false; [Checkbox("Show Icon", spacing = true)] [Order(80)] public bool ShowIcon = true; [Checkbox("Show Icon Cooldown Animation")] [Order(81, collapseWith = nameof(ShowIcon))] public bool ShowIconCooldownAnimation = false; [Checkbox("Change Icon Border When Active")] [Order(82, collapseWith = nameof(ShowIcon))] public bool ChangeIconBorderWhenActive = false; [ColorEdit4("Icon Active Border Color")] [Order(83, collapseWith = nameof(ChangeIconBorderWhenActive))] public PluginConfigColor IconActiveBorderColor = new PluginConfigColor(new Vector4(255f / 255f, 200f / 255f, 35f / 255f, 100f / 100f)); [DragInt("Icon Active Border Thickness", min = 1, max = 10)] [Order(84, collapseWith = nameof(ChangeIconBorderWhenActive))] public int IconActiveBorderThickness = 3; [Checkbox("Change Labels Color When Active", spacing = true)] [Order(85)] public bool ChangeLabelsColorWhenActive = false; [ColorEdit4("Labels Active Color")] [Order(86, collapseWith = nameof(ChangeLabelsColorWhenActive))] public PluginConfigColor LabelsActiveColor = new PluginConfigColor(new Vector4(255f / 255f, 200f / 255f, 35f / 255f, 100f / 100f)); [NestedConfig("Name Label", 100)] public EditableLabelConfig NameLabel = new EditableLabelConfig(new Vector2(5, 0), "[name:initials]", DrawAnchor.Left, DrawAnchor.Left); [NestedConfig("Time Label", 105)] public PartyCooldownTimeLabelConfig TimeLabel = new PartyCooldownTimeLabelConfig(new Vector2(-5, 0), "", DrawAnchor.Right, DrawAnchor.Right); public new static PartyCooldownsBarConfig DefaultConfig() { Vector2 size = new Vector2(150, 24); PartyCooldownsBarConfig config = new PartyCooldownsBarConfig(Vector2.Zero, size, PluginConfigColor.Empty); config.NameLabel.FontID = FontsConfig.DefaultMediumFontKey; config.TimeLabel.FontID = FontsConfig.DefaultMediumFontKey; config.TimeLabel.NumberFormat = 1; return config; } public PartyCooldownsBarConfig(Vector2 position, Vector2 size, PluginConfigColor fillColor, BarDirection fillDirection = BarDirection.Right) : base(position, size, fillColor, fillDirection) { } } public class PartyCooldownTimeLabelConfig : NumericLabelConfig { public PartyCooldownTimeLabelConfig(Vector2 position, string text, DrawAnchor frameAnchor, DrawAnchor textAnchor) : base(position, text, frameAnchor, textAnchor) { } [Checkbox("Show Effect Duration", spacing = true)] [Order(70)] public bool ShowEffectDuration = true; [Checkbox("Show Remainin Cooldown")] [Order(71)] public bool ShowRemainingCooldown = true; } [Exportable(false)] [Disableable(false)] [Section("Party Cooldowns", true)] [SubSection("Cooldowns Tracked", 0)] public class PartyCooldownsDataConfig : PluginConfigObject { public List Cooldowns = new List(); private List _removedIds = new() { 7398 }; private JobRoles _roleFilter = JobRoles.Unknown; private uint _tankFilter = 0; private uint _healerFilter = 0; private uint _meleeFilter = 0; private uint _rangedFilter = 0; private uint _casterFilter = 0; private bool _needsPopupOpen = false; private PartyCooldownData? _popupCooldown = null; public const int ColumnCount = 5; public delegate void CooldownsDataChangedEventHandler(PartyCooldownsDataConfig sender); public event CooldownsDataChangedEventHandler? CooldownsDataChangedEvent; public delegate void CooldownsDataEnabledChangedEventHandler(PartyCooldownsDataConfig sender); public event CooldownsDataEnabledChangedEventHandler? CooldownsDataEnabledChangedEvent; public new static PartyCooldownsDataConfig DefaultConfig() => new PartyCooldownsDataConfig(); private string[] _enabledOptions = new string[] { "Enabled", "Party Cooldowns Only", "Party Frames Only", "Disabled" }; public void UpdateDataIfNeeded() { bool needsSave = false; // remove old cooldowns that are not valid anymore foreach (uint id in _removedIds) { PartyCooldownData? data = Cooldowns.FirstOrDefault(data => data.ActionId == id); if (data != null) { Cooldowns.Remove(data); } } // update data using the game files foreach (uint key in DefaultCooldowns.Keys) { PartyCooldownData? data = Cooldowns.FirstOrDefault(data => data.ActionId == key); PartyCooldownData defaultData = DefaultCooldowns[key]; if (data == null) { Cooldowns.Add(defaultData); needsSave = true; } else if (data != null && !data.Equals(defaultData)) { data.RequiredLevel = defaultData.RequiredLevel; data.DisabledAfterLevel = defaultData.DisabledAfterLevel; data.JobId = defaultData.JobId; data.JobIds = defaultData.JobIds; data.Role = defaultData.Role; data.Roles = defaultData.Roles; data.CooldownDuration = defaultData.CooldownDuration; data.EffectDuration = defaultData.EffectDuration; data.SharedActionIds = defaultData.SharedActionIds; needsSave = true; } } ExcelSheet? sheet = Plugin.DataManager.GetExcelSheet(); ExcelSheet? descriptionsSheet = Plugin.DataManager.GetExcelSheet(); foreach (PartyCooldownData cooldown in Cooldowns) { LuminaAction? action = sheet?.GetRow(cooldown.ActionId); if (!action.HasValue) { continue; } // get real cooldown from data // keep hardcoded value for technical finish if (action.Value.Recast100ms > 0 && cooldown.ActionId != 16004) { cooldown.CooldownDuration = action.Value.Recast100ms / 10; } // not happy about this but didn't want to over-complicate things // special case for troubadour, shield samba and tactician if (cooldown.ActionId == 7405 || cooldown.ActionId == 16012 || cooldown.ActionId == 16889) { cooldown.OverriddenCooldownText = "90-120"; } // reprisal, feint, addle else if (cooldown.ActionId == 7535 || cooldown.ActionId == 7549 || cooldown.ActionId == 7560) { cooldown.OverriddenDurationText = "10-15"; } // swiftcast else if (cooldown.ActionId == 7561) { cooldown.OverriddenCooldownText = "40-60"; } // special case for starry muse if (cooldown.ActionId == 35349) { cooldown.IconId = 3826; } else { cooldown.IconId = action.Value.Icon; } cooldown.Name = action.Value.Name.ToString(); } if (needsSave) { ConfigurationManager.Instance.SaveConfigurations(true); } } [ManualDraw] public bool Draw(ref bool changed) { ImGuiHelper.NewLineAndTab(); // filter ImGui.SameLine(); ImGui.Text("Filter: "); DrawFilter("All", JobRoles.Unknown); DrawFilter("Tanks", JobRoles.Tank); DrawFilter("Healers", JobRoles.Healer); DrawFilter("Melee", JobRoles.DPSMelee); DrawFilter("Ranged", JobRoles.DPSRanged); DrawFilter("Casters", JobRoles.DPSCaster); DrawJobFilters(); // table ImGuiHelper.NewLineAndTab(); ImGui.SameLine(); var flags = ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders | ImGuiTableFlags.BordersOuter | ImGuiTableFlags.BordersInner | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedSame; ExcelSheet? sheet = Plugin.DataManager.GetExcelSheet(); var iconSize = new Vector2(30, 30); if (ImGui.BeginTable("##DelvUI_PartyCooldownsTable", 8, flags, new Vector2(900, 500))) { ImGui.TableSetupColumn("Enabled", ImGuiTableColumnFlags.WidthStretch, 22, 0); ImGui.TableSetupColumn("Icon", ImGuiTableColumnFlags.WidthStretch, 5, 1); ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch, 24, 2); ImGui.TableSetupColumn("Cooldown", ImGuiTableColumnFlags.WidthStretch, 8, 3); ImGui.TableSetupColumn("Duration", ImGuiTableColumnFlags.WidthStretch, 8, 4); ImGui.TableSetupColumn("Priority", ImGuiTableColumnFlags.WidthStretch, 11, 5); ImGui.TableSetupColumn("Section", ImGuiTableColumnFlags.WidthStretch, 11, 6); ImGui.TableSetupColumn("Exclude Jobs", ImGuiTableColumnFlags.WidthStretch, 11, 7); ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableHeadersRow(); foreach (PartyCooldownData cooldown in Cooldowns) { // apply filter if (_roleFilter != JobRoles.Unknown) { if (!cooldown.HasRole(_roleFilter)) { continue; } if (_roleFilter == JobRoles.Tank && _tankFilter != 0 && !cooldown.IsUsableBy(_tankFilter)) { continue; } if (_roleFilter == JobRoles.Healer && _healerFilter != 0 && !cooldown.IsUsableBy(_healerFilter)) { continue; } if (_roleFilter == JobRoles.DPSMelee && _meleeFilter != 0 && !cooldown.IsUsableBy(_meleeFilter)) { continue; } if (_roleFilter == JobRoles.DPSRanged && _rangedFilter != 0 && !cooldown.IsUsableBy(_rangedFilter)) { continue; } if (_roleFilter == JobRoles.DPSCaster && _casterFilter != 0 && !cooldown.IsUsableBy(_casterFilter)) { continue; } } LuminaAction? action = sheet?.GetRow(cooldown.ActionId); ImGui.PushID(cooldown.ActionId.ToString()); ImGui.TableNextRow(ImGuiTableRowFlags.None, iconSize.Y); // enabled if (ImGui.TableSetColumnIndex(0)) { ImGui.PushItemWidth(178); ImGui.SetCursorPos(new Vector2(ImGui.GetCursorPosX() + 2, ImGui.GetCursorPosY() + 2)); int enabled = (int)cooldown.EnabledV2; if (ImGui.Combo($"##{cooldown.ActionId}_enabled", ref enabled, _enabledOptions)) { changed = true; cooldown.EnabledV2 = (PartyCooldownEnabled)enabled; CooldownsDataEnabledChangedEvent?.Invoke(this); } } // icon if (ImGui.TableSetColumnIndex(1) && action != null) { ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 3); DrawHelper.DrawIcon(action, ImGui.GetCursorPos(), iconSize, false, false); } // name if (ImGui.TableSetColumnIndex(2)) { ImGui.Text(cooldown.Name); } // cooldown if (ImGui.TableSetColumnIndex(3)) { string cooldownText = cooldown.OverriddenCooldownText != null ? cooldown.OverriddenCooldownText : $"{cooldown.CooldownDuration}"; ImGui.Text(cooldownText); } // duration if (ImGui.TableSetColumnIndex(4)) { string durationText = cooldown.OverriddenDurationText != null ? cooldown.OverriddenDurationText : $"{cooldown.EffectDuration}"; ImGui.Text(durationText); } // priority if (ImGui.TableSetColumnIndex(5)) { ImGui.PushItemWidth(86); ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 2); if (ImGui.DragInt($"##{cooldown.ActionId}_priority", ref cooldown.Priority, 1, 0, 100)) { cooldown.Priority = Math.Clamp(cooldown.Priority, 0, 100); changed = true; CooldownsDataChangedEvent?.Invoke(this); } ImGuiHelper.SetTooltip("Priority determines which cooldows show first on the list."); } // column if (ImGui.TableSetColumnIndex(6)) { ImGui.PushItemWidth(86); ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 2); if (ImGui.DragInt($"##{cooldown.ActionId}_column", ref cooldown.Column, 0.1f, 1, ColumnCount, "%i", ImGuiSliderFlags.NoInput)) { changed = true; CooldownsDataChangedEvent?.Invoke(this); } ImGuiHelper.SetTooltip("Allows to separate cooldowns in different columns."); } // exlude if (ImGui.TableSetColumnIndex(7) && (cooldown.Roles != null || cooldown.Role != JobRoles.Unknown)) { ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 2); if (ImGui.Button("Select##Exclude", new Vector2(86, 24))) { _needsPopupOpen = true; _popupCooldown = cooldown; } } ImGui.PopID(); } ImGui.EndTable(); } if (_needsPopupOpen) { ImGui.OpenPopup("Exclude Jobs##HSUI"); _needsPopupOpen = false; } if (_popupCooldown != null) { DrawJobsListForCooldown(_popupCooldown); } return false; } private List IgnoredJobIds = new List() { JobIDs.GLA, JobIDs.MRD, JobIDs.CNJ, JobIDs.WHM, JobIDs.PGL, JobIDs.LNC, JobIDs.ROG, JobIDs.ARC, JobIDs.THM, JobIDs.ACN, JobIDs.BLU, }; private void DrawJobsListForCooldown(PartyCooldownData cooldown) { List roles = cooldown.Roles ?? new List() { cooldown.Role }; List jobIds = new List(); foreach (JobRoles role in roles) { jobIds.AddRange(JobsHelper.JobsByRole[role]); } jobIds = jobIds.Where(id => !IgnoredJobIds.Contains(id)).ToList(); string title = cooldown.Name + ":"; float width = Math.Max(100, ImGui.CalcTextSize(title).X + 10); ImGui.SetNextWindowSize(new(width, jobIds.Count * 30 + 32)); if (ImGui.BeginPopup("Exclude Jobs##HSUI", ImGuiWindowFlags.NoMove)) { ImGui.Text(title); foreach (uint jobId in jobIds) { bool selected = !cooldown.ExcludedJobIds.Contains(jobId); if (ImGui.Checkbox(JobsHelper.JobNames[jobId] + "##popup", ref selected)) { if (selected) { cooldown.ExcludedJobIds.Remove(jobId); } else { cooldown.ExcludedJobIds.Add(jobId); } CooldownsDataEnabledChangedEvent?.Invoke(this); } } ImGui.EndPopup(); } } private void DrawFilter(string name, JobRoles role) { ImGui.SameLine(); if (ImGui.RadioButton(name, _roleFilter == role)) { _roleFilter = role; } } private void DrawJobFilter(string name, uint job, ref uint filter) { ImGui.SameLine(); if (ImGui.RadioButton(name, filter == job)) { filter = job; } } private void DrawJobFilters() { if (_roleFilter == JobRoles.Unknown) { return; } ImGui.Text("\t\t\t\t "); if (_roleFilter == JobRoles.Tank) { DrawJobFilter("All##tank", 0, ref _tankFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.PLD], JobIDs.PLD, ref _tankFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.WAR], JobIDs.WAR, ref _tankFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.DRK], JobIDs.DRK, ref _tankFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.GNB], JobIDs.GNB, ref _tankFilter); } else if (_roleFilter == JobRoles.Healer) { DrawJobFilter("All##healer", 0, ref _healerFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.WHM], JobIDs.WHM, ref _healerFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.SCH], JobIDs.SCH, ref _healerFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.AST], JobIDs.AST, ref _healerFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.SGE], JobIDs.SGE, ref _healerFilter); } else if (_roleFilter == JobRoles.DPSMelee) { DrawJobFilter("All##melee", 0, ref _meleeFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.MNK], JobIDs.MNK, ref _meleeFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.DRG], JobIDs.DRG, ref _meleeFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.NIN], JobIDs.NIN, ref _meleeFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.SAM], JobIDs.SAM, ref _meleeFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.RPR], JobIDs.RPR, ref _meleeFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.VPR], JobIDs.VPR, ref _meleeFilter); } else if (_roleFilter == JobRoles.DPSRanged) { DrawJobFilter("All##ranged", 0, ref _rangedFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.BRD], JobIDs.BRD, ref _rangedFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.MCH], JobIDs.MCH, ref _rangedFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.DNC], JobIDs.DNC, ref _rangedFilter); } else if (_roleFilter == JobRoles.DPSCaster) { DrawJobFilter("All##caster", 0, ref _casterFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.BLM], JobIDs.BLM, ref _casterFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.SMN], JobIDs.SMN, ref _casterFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.RDM], JobIDs.RDM, ref _casterFilter); DrawJobFilter(JobsHelper.JobNames[JobIDs.PCT], JobIDs.PCT, ref _casterFilter); } } public static Dictionary DefaultCooldowns = new Dictionary() { // PARTY-WIDE EFFECTS // Default columns: // 1. Role mitigations (Reprisal, Addle, Feint) // 2. Job specific mitigations // 3. Damage buffs / burst window cooldowns // 4. Personal mitigations / immunities / externals // 5. Misc (Provoke, Shirk, Switcast, Rescue, etc) // TANKS ------------------------------------------------------------------------------------------------- [7535] = NewData(7535, JobRoles.Tank, 22, 60, 10, 100, 1, PartyCooldownEnabled.PartyFrames), // reprisal [7531] = NewData(7531, JobRoles.Tank, 8, 90, 20, 50, 4, PartyCooldownEnabled.PartyFrames), // rampart [7533] = NewData(7533, JobRoles.Tank, 15, 30, 1, 50, 5, PartyCooldownEnabled.PartyFrames), // provoke [7537] = NewData(7537, JobRoles.Tank, 48, 120, 1, 50, 5, PartyCooldownEnabled.PartyFrames), // shirk // PLD [30] = NewData(30, JobIDs.PLD, 50, 420, 10, 100, 4, PartyCooldownEnabled.PartyFrames), // hallowed ground [3540] = NewData(3540, JobIDs.PLD, 56, 90, 30, 90, 2, PartyCooldownEnabled.PartyFrames), // divine veil [7385] = NewData(7385, JobIDs.PLD, 70, 120, 18, 90, 2, PartyCooldownEnabled.PartyFrames), // passage of arms [20] = NewData(20, JobIDs.PLD, 2, 60, 20, 10, 3, PartyCooldownEnabled.PartyFrames), // fight or flight [22] = NewData(22, JobIDs.PLD, 52, 90, 10, 90, 4, PartyCooldownEnabled.PartyFrames), // bulwark [27] = NewData(27, JobIDs.PLD, 45, 120, 12, 90, 4, PartyCooldownEnabled.PartyFrames), // cover [17] = NewData(17, JobIDs.PLD, 38, 120, 15, 90, 4, PartyCooldownEnabled.PartyFrames, disabledAfterLevel: 92), // sentinel [36920] = NewData(36920, JobIDs.PLD, 92, 120, 15, 90, 4, PartyCooldownEnabled.PartyFrames), // guardian // WAR [43] = NewData(43, JobIDs.WAR, 42, 240, 10, 100, 4, PartyCooldownEnabled.PartyFrames), // holmgang [7388] = NewData(7388, JobIDs.WAR, 68, 90, 30, 90, 2, PartyCooldownEnabled.PartyFrames), // shake it off [52] = NewData(52, JobIDs.WAR, 50, 60, 30, 10, 3, PartyCooldownEnabled.PartyFrames), // infuriate [38] = NewData(38, JobIDs.WAR, 6, 60, 30, 10, 3, PartyCooldownEnabled.PartyFrames, disabledAfterLevel: 70), // berserk [7389] = NewData(7389, JobIDs.WAR, 70, 60, 30, 10, 3, PartyCooldownEnabled.PartyFrames), // inner release [40] = NewData(40, JobIDs.WAR, 30, 90, 10, 90, 4, PartyCooldownEnabled.PartyFrames), // thrill of battle [3552] = NewData(3552, JobIDs.WAR, 58, 60, 15, 90, 4, PartyCooldownEnabled.PartyFrames), // equilibrium [3551] = NewData(3551, JobIDs.WAR, 56, 25, 8, 90, 4, PartyCooldownEnabled.PartyFrames, disabledAfterLevel: 82, sharedActionIds: new() { 16464 }), // raw intuition [25751] = NewData(25751, JobIDs.WAR, 82, 25, 8, 90, 4, PartyCooldownEnabled.PartyFrames, sharedActionIds: new() { 16464 }), // bloodwhetting [16464] = NewData(16464, JobIDs.WAR, 76, 25, 8, 90, 4, PartyCooldownEnabled.PartyFrames, sharedActionIds: new() { 3551, 25751 }), // nascent flash [44] = NewData(44, JobIDs.WAR, 38, 120, 15, 90, 4, PartyCooldownEnabled.PartyFrames, disabledAfterLevel: 92), // vengeance [36923] = NewData(36923, JobIDs.WAR, 92, 120, 15, 90, 4, PartyCooldownEnabled.PartyFrames), // damnation // DRK [3638] = NewData(3638, JobIDs.DRK, 50, 300, 10, 100, 4, PartyCooldownEnabled.PartyFrames), // living dead [16471] = NewData(16471, JobIDs.DRK, 66, 90, 15, 90, 2, PartyCooldownEnabled.PartyFrames), // dark missionary [16472] = NewData(16472, JobIDs.DRK, 80, 120, 20, 10, 3, PartyCooldownEnabled.PartyFrames), // living shadow [3634] = NewData(3634, JobIDs.DRK, 45, 60, 10, 90, 4, PartyCooldownEnabled.PartyFrames), // dark mind [25754] = NewData(25754, JobIDs.DRK, 82, 60, 10, 90, 4, PartyCooldownEnabled.PartyFrames), // oblation [3625] = NewData(3625, JobIDs.DRK, 35, 60, 15, 10, 3, PartyCooldownEnabled.PartyFrames, disabledAfterLevel: 68), // blood weapon [7390] = NewData(7390, JobIDs.DRK, 68, 60, 15, 10, 3, PartyCooldownEnabled.PartyFrames), // delirium [3636] = NewData(3636, JobIDs.DRK, 38, 120, 15, 90, 4, PartyCooldownEnabled.PartyFrames, disabledAfterLevel: 92), // shadow wall [36927] = NewData(36927, JobIDs.DRK, 92, 120, 15, 90, 4, PartyCooldownEnabled.PartyFrames), // shadowed vigil // GNB [16152] = NewData(16152, JobIDs.GNB, 50, 360, 10, 100, 4, PartyCooldownEnabled.PartyFrames), // superbolide [16160] = NewData(16160, JobIDs.GNB, 64, 90, 15, 90, 2, PartyCooldownEnabled.PartyFrames), // heart of light [16164] = NewData(16164, JobIDs.GNB, 76, 120, 1, 10, 3, PartyCooldownEnabled.PartyFrames), // bloodfest [16138] = NewData(16138, JobIDs.GNB, 2, 60, 20, 10, 3, PartyCooldownEnabled.PartyFrames), // no mercy [16140] = NewData(16140, JobIDs.GNB, 6, 90, 20, 90, 4, PartyCooldownEnabled.PartyFrames), // camouflage [16161] = NewData(16161, JobIDs.GNB, 68, 25, 7, 90, 4, PartyCooldownEnabled.PartyFrames, disabledAfterLevel: 82), // heart of stone [25758] = NewData(25758, JobIDs.GNB, 82, 25, 7, 90, 4, PartyCooldownEnabled.PartyFrames), // heart of corundum [16148] = NewData(16148, JobIDs.GNB, 38, 120, 15, 90, 4, PartyCooldownEnabled.PartyFrames, disabledAfterLevel: 92), // nebula [36935] = NewData(36935, JobIDs.GNB, 92, 120, 15, 90, 4, PartyCooldownEnabled.PartyFrames), // great nebula // HEALER ------------------------------------------------------------------------------------------------- [7571] = NewData(7571, JobRoles.Healer, 48, 120, 0, 80, 5, PartyCooldownEnabled.Disabled), // rescue // AST [16552] = NewData(16552, JobIDs.AST, 50, 120, 20, 30, 3, PartyCooldownEnabled.PartyCooldowns), // divination [3613] = NewData(3613, JobIDs.AST, 58, 60, 18, 80, 2, PartyCooldownEnabled.PartyFrames), // collective unconscious [16553] = NewData(16553, JobIDs.AST, 60, 60, 15, 80, 2, PartyCooldownEnabled.PartyFrames), // celestial opposition [7439] = NewData(7439, JobIDs.AST, 62, 60, 20, 80, 2, PartyCooldownEnabled.PartyFrames), // earthly star (stellar detonation = 8324) [16559] = NewData(16559, JobIDs.AST, 80, 120, 20, 80, 2, PartyCooldownEnabled.PartyFrames), // neutral sect [25873] = NewData(25873, JobIDs.AST, 86, 60, 8, 80, 2, PartyCooldownEnabled.PartyFrames), // exaltation [25874] = NewData(25874, JobIDs.AST, 90, 180, 15, 80, 2, PartyCooldownEnabled.PartyFrames), // macrocosmos (microcosmos = 25875) [3606] = NewData(3606, JobIDs.AST, 6, 90, 15, 10, 4, PartyCooldownEnabled.PartyFrames), // lightspeed // SCH [7436] = NewData(7436, JobIDs.SCH, 66, 120, 20, 30, 3, PartyCooldownEnabled.PartyCooldowns), // chain stratagem [805] = NewData(805, JobIDs.SCH, 40, 120, 20, 50, 2, PartyCooldownEnabled.PartyFrames), // fey illumination [188] = NewData(188, JobIDs.SCH, 50, 30, 15, 80, 2, PartyCooldownEnabled.PartyFrames), // sacred soil [3585] = NewData(3585, JobIDs.SCH, 56, 90, 1, 80, 2, PartyCooldownEnabled.PartyFrames), // deployment tactics [25867] = NewData(25867, JobIDs.SCH, 86, 60, 10, 80, 2, PartyCooldownEnabled.PartyFrames), // protraction [25868] = NewData(25868, JobIDs.SCH, 90, 120, 20, 80, 2, PartyCooldownEnabled.PartyFrames), // expedient [7434] = NewData(7434, JobIDs.SCH, 62, 45, 1, 50, 4, PartyCooldownEnabled.PartyFrames), // excogitation [37014] = NewData(37014, JobIDs.SCH, 100, 180, 20, 80, 2, PartyCooldownEnabled.PartyFrames), // seraphism // WHM [16536] = NewData(16536, JobIDs.WHM, 80, 120, 20, 80, 2, PartyCooldownEnabled.PartyFrames), // temperance [3569] = NewData(3569, JobIDs.WHM, 52, 90, 24, 50, 2, PartyCooldownEnabled.PartyFrames), // asylum [25862] = NewData(25862, JobIDs.WHM, 90, 180, 15, 80, 2, PartyCooldownEnabled.PartyFrames), // liturgy of the bell [136] = NewData(136, JobIDs.WHM, 30, 120, 15, 10, 4, PartyCooldownEnabled.PartyFrames), // presence of mind [140] = NewData(140, JobIDs.WHM, 50, 180, 1, 10, 4, PartyCooldownEnabled.PartyFrames), // benediction [3570] = NewData(3570, JobIDs.WHM, 60, 60, 1, 10, 4, PartyCooldownEnabled.PartyFrames), // tetragrammaton [25861] = NewData(25861, JobIDs.WHM, 86, 60, 8, 10, 4, PartyCooldownEnabled.PartyFrames), // aquaveil // SGE [24298] = NewData(24298, JobIDs.SGE, 50, 30, 15, 80, 2, PartyCooldownEnabled.PartyFrames), // kerachole [24302] = NewData(24302, JobIDs.SGE, 60, 60, 15, 80, 2, PartyCooldownEnabled.PartyFrames), // physis ii [24310] = NewData(24310, JobIDs.SGE, 76, 120, 20, 80, 2, PartyCooldownEnabled.PartyFrames), // holos [24311] = NewData(24311, JobIDs.SGE, 80, 120, 15, 80, 2, PartyCooldownEnabled.PartyFrames), // panhaima [24318] = NewData(24318, JobIDs.SGE, 90, 120, 1, 80, 2, PartyCooldownEnabled.PartyFrames), // pneuma [24303] = NewData(24303, JobIDs.SGE, 62, 45, 15, 10, 4, PartyCooldownEnabled.PartyFrames), // taurochole [24305] = NewData(24305, JobIDs.SGE, 70, 120, 15, 10, 4, PartyCooldownEnabled.PartyFrames), // haima [24317] = NewData(24317, JobIDs.SGE, 86, 60, 10, 10, 4, PartyCooldownEnabled.PartyFrames), // krasis [37035] = NewData(37035, JobIDs.SGE, 100, 180, 20, 80, 2, PartyCooldownEnabled.PartyFrames), // philosophia // MELEE ------------------------------------------------------------------------------------------------- [7549] = NewData(7549, JobRoles.DPSMelee, 22, 90, 10, 100, 1, PartyCooldownEnabled.PartyFrames), // feint [7542] = NewData(7542, JobRoles.DPSMelee, 12, 90, 20, 10, 4, PartyCooldownEnabled.PartyFrames), // bloodbath // SAM // lol? // VPR // lol? // NIN [2248] = NewData(2248, JobIDs.NIN, 15, 120, 20, 30, 3, PartyCooldownEnabled.PartyFrames, disabledAfterLevel: 68), // mug [36957] = NewData(36957, JobIDs.NIN, 68, 120, 20, 30, 3, PartyCooldownEnabled.PartyCooldowns), // dokumori [2258] = NewData(2258, JobIDs.NIN, 18, 60, 15, 10, 3, PartyCooldownEnabled.PartyFrames, disabledAfterLevel: 92), // trick attack [36958] = NewData(36958, JobIDs.NIN, 92, 60, 15, 10, 3, PartyCooldownEnabled.PartyFrames), // kunai's bane [2241] = NewData(2241, JobIDs.NIN, 2, 120, 20, 20, 4, PartyCooldownEnabled.PartyFrames), // shade shift // DRG [3557] = NewData(3557, JobIDs.DRG, 52, 120, 20, 30, 3, PartyCooldownEnabled.PartyCooldowns), // battle litany [85] = NewData(85, JobIDs.DRG, 30, 60, 20, 10, 3, PartyCooldownEnabled.PartyFrames), // lance charge // MNK [65] = NewData(65, JobIDs.MNK, 42, 90, 15, 50, 2, PartyCooldownEnabled.PartyFrames), // mantra [7396] = NewData(7396, JobIDs.MNK, 70, 120, 20, 90, 3, PartyCooldownEnabled.PartyCooldowns), // brotherhood [7395] = NewData(7395, JobIDs.MNK, 68, 60, 20, 10, 3, PartyCooldownEnabled.PartyFrames), // riddle of fire [7394] = NewData(7394, JobIDs.MNK, 64, 120, 15, 20, 4, PartyCooldownEnabled.PartyFrames), // riddle of earth // RPR [24405] = NewData(24405, JobIDs.RPR, 72, 120, 20, 30, 3, PartyCooldownEnabled.PartyCooldowns), // arcane circle [24404] = NewData(24404, JobIDs.RPR, 40, 30, 5, 10, 4, PartyCooldownEnabled.PartyFrames), // arcane crest // RANGED ------------------------------------------------------------------------------------------------- // BRD [118] = NewData(118, JobIDs.BRD, 50, 120, 20, 30, 3, PartyCooldownEnabled.PartyCooldowns), // battle voice [7405] = NewData(7405, JobIDs.BRD, 62, 90, 15, 70, 2, PartyCooldownEnabled.PartyFrames, "90-120"), // troubadour [7408] = NewData(7408, JobIDs.BRD, 66, 120, 15, 40, 2, PartyCooldownEnabled.PartyFrames), // nature's minne [25785] = NewData(25785, JobIDs.BRD, 90, 110, 20, 30, 3, PartyCooldownEnabled.PartyCooldowns), // radiant finale [101] = NewData(101, JobIDs.BRD, 4, 120, 20, 90, 3, PartyCooldownEnabled.PartyFrames), // raging strikes // DNC [16012] = NewData(16012, JobIDs.DNC, 56, 90, 15, 70, 2, PartyCooldownEnabled.PartyFrames, "90-120"), // shield samba [16004] = NewData(16004, JobIDs.DNC, 70, 120, 20, 30, 3, PartyCooldownEnabled.PartyCooldowns), // technical step / finish [16011] = NewData(16011, JobIDs.DNC, 62, 120, 20, 90, 3, PartyCooldownEnabled.PartyFrames), // devilment // MCH [16889] = NewData(16889, JobIDs.MCH, 56, 90, 15, 70, 2, PartyCooldownEnabled.PartyFrames, "90-120"), // tactician [2887] = NewData(2887, JobIDs.MCH, 62, 120, 10, 70, 2, PartyCooldownEnabled.PartyFrames), // dismantle // CASTER ------------------------------------------------------------------------------------------------- [7560] = NewData(7560, JobRoles.DPSCaster, 8, 90, 10, 100, 1, PartyCooldownEnabled.PartyFrames), // addle // RDM [25857] = NewData(25857, JobIDs.RDM, 86, 120, 10, 70, 2, PartyCooldownEnabled.PartyCooldownsAndPartyFrames), // magick barrier [7520] = NewData(7520, JobIDs.RDM, 58, 120, 20, 30, 3, PartyCooldownEnabled.PartyCooldowns), // embolden // SMN [25801] = NewData(25801, JobIDs.SMN, 66, 120, 20, 30, 3, PartyCooldownEnabled.PartyCooldowns), // searing light [25799] = NewData(25799, JobIDs.SMN, 2, 60, 30, 10, 3, PartyCooldownEnabled.PartyFrames), // radiant aegis // BLM [3573] = NewData(3573, JobIDs.BLM, 52, 120, 30, 90, 3, PartyCooldownEnabled.PartyFrames), // ley lines [157] = NewData(157, JobIDs.BLM, 38, 120, 20, 10, 4, PartyCooldownEnabled.PartyFrames), // manaward // PCT [35349] = NewData(35349, JobIDs.PCT, 70, 120, 20, 30, 3, PartyCooldownEnabled.PartyCooldowns), // scenic muse [34685] = NewData(34685, JobIDs.PCT, 10, 120, 10, 10, 4, PartyCooldownEnabled.PartyFrames), // tempera coat // MULTI-ROLE ------------------------------------------------------------------------------------------------- [7541] = NewData(7541, new List() { JobRoles.DPSMelee, JobRoles.DPSRanged }, 8, 120, 0, 80, 4, PartyCooldownEnabled.PartyFrames), // second wind [7561] = NewData(7561, new List() { JobRoles.Healer, JobRoles.DPSCaster }, 18, 60, 1, 80, 5, PartyCooldownEnabled.PartyFrames, null, new HashSet() { JobIDs.BLM, JobIDs.SMN, JobIDs.RDM }), // swiftcast [7562] = NewData(7562, new List() { JobRoles.Healer, JobRoles.DPSCaster }, 14, 60, 21, 80, 5, PartyCooldownEnabled.Disabled), // lucid dreaming }; #region helpers private static PartyCooldownData NewData( uint actionId, uint jobId, uint level, int cooldown, int effectDuration, int priority, int column, PartyCooldownEnabled enabled, string? overriddenCooldownText = null, HashSet? excludedJobIds = null, uint disabledAfterLevel = 0, List? sharedActionIds = null) { PartyCooldownData data = NewData( actionId, level, cooldown, effectDuration, priority, column, enabled, overriddenCooldownText, excludedJobIds, disabledAfterLevel, sharedActionIds ); data.JobId = jobId; data.Role = JobRoles.Unknown; return data; } private static PartyCooldownData NewData(uint actionId, JobRoles role, uint level, int cooldown, int effectDuration, int priority, int column, PartyCooldownEnabled enabled, string? overriddenCooldownText = null, HashSet? excludedJobIds = null) { PartyCooldownData data = NewData( actionId, level, cooldown, effectDuration, priority, column, enabled, overriddenCooldownText, excludedJobIds ); data.JobId = 0; data.Role = role; return data; } private static PartyCooldownData NewData(uint actionId, List roles, uint level, int cooldown, int effectDuration, int priority, int column, PartyCooldownEnabled enabled, string? overriddenCooldownText = null, HashSet? excludedJobIds = null) { PartyCooldownData data = NewData( actionId, level, cooldown, effectDuration, priority, column, enabled, overriddenCooldownText, excludedJobIds ); data.JobId = 0; data.Role = JobRoles.Unknown; data.Roles = roles; return data; } private static PartyCooldownData NewData( uint actionId, uint level, int cooldown, int effectDuration, int priority, int column, PartyCooldownEnabled enabled, string? overriddenCooldownText = null, HashSet? excludedJobIds = null, uint disabledAfterLevel = 0, List? sharedActionIds = null ) { PartyCooldownData data = new PartyCooldownData(); data.ActionId = actionId; data.RequiredLevel = level; data.DisabledAfterLevel = disabledAfterLevel; data.CooldownDuration = cooldown; data.EffectDuration = effectDuration; data.Priority = priority; data.Column = column; data.EnabledV2 = enabled; data.OverriddenCooldownText = overriddenCooldownText; data.SharedActionIds = sharedActionIds ?? new(); if (excludedJobIds != null) { data.ExcludedJobIds = excludedJobIds; } return data; } #endregion } }