using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Bindings.ImGui; using HSUI.Config; using HSUI.Enums; using HSUI.Helpers; using HSUI.Interface.GeneralElements; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; namespace HSUI.Interface.Party { public class AllianceFramesHud : DraggableHudElement, IHudElementWithMouseOver, IHudElementWithPreview, IHudElementWithVisibilityConfig { private AllianceFramesConfigBase Config => (AllianceFramesConfigBase)_config; public VisibilityConfig VisibilityConfig => Config.VisibilityConfig; private PartyFramesConfigs Configs; private Vector2 _contentMargin = new Vector2(2, 2); private static readonly int MaxMemberCount = 8; private Vector2 _origin; private LayoutInfo _layoutInfo; private uint _memberCount = 0; private bool _layoutDirty = true; private readonly List bars; private LabelHud _titleLabelHud; public AllianceFramesHud(AllianceFramesConfigBase config, string displayName) : base(config, displayName) { Configs = PartyFramesConfigs.GetAllianceConfigs(); config.ValueChangeEvent += OnLayoutPropertyChanged; Configs.HealthBar.ValueChangeEvent += OnLayoutPropertyChanged; Configs.HealthBar.ColorsConfig.ValueChangeEvent += OnLayoutPropertyChanged; bars = new List(MaxMemberCount); for (int i = 0; i < bars.Capacity; i++) { var bar = new PartyFramesBar("HSUI_allianceFramesBar_" + Config.SlotIndex + "_" + i, Configs); bars.Add(bar); } _titleLabelHud = new LabelHud(Config.TitleLabelConfig); AllianceManager.Instance.MembersChangedEvent += OnMembersChanged; } protected override void InternalDispose() { foreach (var bar in bars) { try { bar.Dispose(); } catch (Exception ex) { Plugin.Logger.Error($"Error disposing AllianceFramesBar: {ex.Message}"); } } bars.Clear(); _config.ValueChangeEvent -= OnLayoutPropertyChanged; Configs.HealthBar.ValueChangeEvent -= OnLayoutPropertyChanged; Configs.HealthBar.ColorsConfig.ValueChangeEvent -= OnLayoutPropertyChanged; AllianceManager.Instance.MembersChangedEvent -= OnMembersChanged; } private void OnLayoutPropertyChanged(object sender, OnChangeBaseArgs args) { if (args.PropertyName == "Size" || args.PropertyName == "FillRowsFirst" || args.PropertyName == "BarsAnchor" || args.PropertyName == "Padding" || args.PropertyName == "Rows" || args.PropertyName == "Columns") { _layoutDirty = true; } } private void OnMembersChanged(AllianceManager sender) { _layoutDirty = true; } private void UpdateBars(Vector2 contentStartPos) { var members = AllianceManager.Instance.GetMembersForOtherAlliance(Config.SlotIndex); uint memberCount = (uint)members.Count; uint row = 0, col = 0; CalculateBarPosition(contentStartPos, Size, out float x, out float y); for (int i = 0; i < bars.Count; i++) { var bar = bars[i]; if (i >= memberCount) { bar.Visible = false; continue; } bar.Member = members[i]; bar.Visible = true; bar.Position = new Vector2( x + Configs.HealthBar.Size.X * col + (Configs.HealthBar.Padding.X - 1) * col, y + Configs.HealthBar.Size.Y * row + (Configs.HealthBar.Padding.Y - 1) * row ); if (Config.FillRowsFirst) { col++; if (col >= _layoutInfo.TotalColCount) { col = 0; row++; } } else { row++; if (row >= _layoutInfo.TotalRowCount) { row = 0; col++; } } } } private void UpdateBarsPosition(Vector2 delta) { foreach (var bar in bars) bar.Position = bar.Position + delta; } private void CalculateBarPosition(Vector2 position, Vector2 spaceSize, out float x, out float y) { x = position.X; y = position.Y; if (Config.BarsAnchor == DrawAnchor.Top || Config.BarsAnchor == DrawAnchor.Center || Config.BarsAnchor == DrawAnchor.Bottom) x += (spaceSize.X - _layoutInfo.ContentSize.X) / 2f; else if (Config.BarsAnchor == DrawAnchor.TopRight || Config.BarsAnchor == DrawAnchor.Right || Config.BarsAnchor == DrawAnchor.BottomRight) x += spaceSize.X - _layoutInfo.ContentSize.X; if (Config.BarsAnchor == DrawAnchor.Left || Config.BarsAnchor == DrawAnchor.Center || Config.BarsAnchor == DrawAnchor.Right) y += (spaceSize.Y - _layoutInfo.ContentSize.Y) / 2f; else if (Config.BarsAnchor == DrawAnchor.BottomLeft || Config.BarsAnchor == DrawAnchor.Bottom || Config.BarsAnchor == DrawAnchor.BottomRight) y += spaceSize.Y - _layoutInfo.ContentSize.Y; } private Vector2 Size => new Vector2( Config.Columns * Configs.HealthBar.Size.X + (Config.Columns - 1) * Configs.HealthBar.Padding.X, Config.Rows * Configs.HealthBar.Size.Y + (Config.Rows - 1) * Configs.HealthBar.Padding.Y ); private void UpdateLayout(Vector2 origin) { var members = AllianceManager.Instance.GetMembersForOtherAlliance(Config.SlotIndex); uint count = (uint)members.Count; Vector2 contentStartPos = origin + Config.Position; if (_layoutDirty || _memberCount != count) { _layoutInfo = LayoutHelper.CalculateLayout( Size, Configs.HealthBar.Size, count, Configs.HealthBar.Padding, Config.FillRowsFirst ); UpdateBars(contentStartPos); } else if (_origin != contentStartPos) { UpdateBarsPosition(contentStartPos - _origin); } _layoutDirty = false; _origin = contentStartPos; _memberCount = count; } public void StopPreview() { Config.Preview = false; foreach (var bar in bars) bar.StopPreview(); } protected override (List, List) ChildrenPositionsAndSizes() { return (new List() { Config.Position + Size / 2f }, new List() { Size }); } public void StopMouseover() { foreach (var bar in bars) bar.StopMouseover(); } public override void DrawChildren(Vector2 origin) { if (!_config.Enabled) return; bool inRaid = AllianceManager.Instance.IsInAllianceRaid; var members = AllianceManager.Instance.GetMembersForOtherAlliance(Config.SlotIndex); if (!inRaid && !Config.Preview && !Config.ShowWhenNotInRaid) return; if (members.Count < 1 && !Config.Preview && !Config.ShowWhenNotInRaid) return; // Show placeholder when not in raid (for positioning) or when Preview is on if ((Config.Preview || Config.ShowWhenNotInRaid) && members.Count < 1) { _layoutInfo = LayoutHelper.CalculateLayout(Size, Configs.HealthBar.Size, 8, Configs.HealthBar.Padding, Config.FillRowsFirst); for (int i = 0; i < 8; i++) { bars[i].Member = new FakePartyFramesMember(i); bars[i].Visible = true; } for (int i = 8; i < bars.Count; i++) bars[i].Visible = false; CalculateBarPosition(origin + Config.Position, Size, out float px, out float py); uint row = 0, col = 0; for (int i = 0; i < 8; i++) { bars[i].Position = new Vector2( px + Configs.HealthBar.Size.X * col + (Configs.HealthBar.Padding.X - 1) * col, py + Configs.HealthBar.Size.Y * row + (Configs.HealthBar.Padding.Y - 1) * row ); if (Config.FillRowsFirst) { col++; if (col >= _layoutInfo.TotalColCount) { col = 0; row++; } } else { row++; if (row >= _layoutInfo.TotalRowCount) { row = 0; col++; } } } AddDrawAction(StrataLevel.LOWEST, () => { var drawList = ImGui.GetWindowDrawList(); Vector2 bgPos = origin + Config.Position - _contentMargin; Vector2 bgSize = Size + _contentMargin * 2; drawList.AddRectFilled(bgPos, bgPos + bgSize, 0x66000000); drawList.AddRect(bgPos, bgPos + bgSize, 0x66FFFFFF); }); for (int i = 0; i < 8; i++) AddDrawActions(bars[i].GetBarDrawActions(origin)); foreach (var bar in bars) AddDrawActions(bar.GetElementsDrawActions(origin)); Config.TitleLabelConfig.SetText(Config.FramesLabel + " (Preview)"); AddDrawAction(Config.TitleLabelConfig.StrataLevel, () => _titleLabelHud.Draw(origin + Config.Position)); return; } if (Config.Preview) { AddDrawAction(StrataLevel.LOWEST, () => { var drawList = ImGui.GetWindowDrawList(); Vector2 bgPos = origin + Config.Position - _contentMargin; Vector2 bgSize = Size + _contentMargin * 2; drawList.AddRectFilled(bgPos, bgPos + bgSize, 0x66000000); drawList.AddRect(bgPos, bgPos + bgSize, 0x66FFFFFF); }); } UpdateLayout(origin); IGameObject? target = Plugin.TargetManager.SoftTarget ?? Plugin.TargetManager.Target; int targetIndex = -1; for (int i = 0; i < members.Count; i++) { var member = bars[i].Member; if (member != null && target != null && member.ObjectId == target.GameObjectId) { targetIndex = i; continue; } AddDrawActions(bars[i].GetBarDrawActions(origin)); } if (targetIndex >= 0) AddDrawActions(bars[targetIndex].GetBarDrawActions(origin, Configs.HealthBar.ColorsConfig.TargetBordercolor)); foreach (var bar in bars) AddDrawActions(bar.GetElementsDrawActions(origin)); string titleText = Config.FramesLabel; if (inRaid && AllianceManager.Instance.TryGetAllianceIndexForSlot(Config.SlotIndex, out int allianceIdx) && AllianceManager.Instance.TryGetAllianceLetter(allianceIdx, out string letter)) { titleText = $"Alliance {letter}"; } Config.TitleLabelConfig.SetText(titleText); AddDrawAction(Config.TitleLabelConfig.StrataLevel, () => { _titleLabelHud.Draw(origin + Config.Position); }); } } }