Initial release: HSUI v1.0.0.0 - HUD replacement with configurable hotbars
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,465 @@
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using HSUI.Config;
|
||||
using HSUI.Enums;
|
||||
using HSUI.Helpers;
|
||||
using HSUI.Interface.GeneralElements;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
|
||||
namespace HSUI.Interface.Party
|
||||
{
|
||||
public class PartyFramesHud : DraggableHudElement, IHudElementWithMouseOver, IHudElementWithPreview, IHudElementWithVisibilityConfig
|
||||
{
|
||||
private PartyFramesConfig Config => (PartyFramesConfig)_config;
|
||||
public VisibilityConfig VisibilityConfig => Config.VisibilityConfig;
|
||||
private PartyFramesConfigs Configs;
|
||||
|
||||
private Vector2 _contentMargin = new Vector2(2, 2);
|
||||
private static readonly int MaxMemberCount = 9; // 8 players + chocobo
|
||||
|
||||
// layout
|
||||
private Vector2 _origin;
|
||||
private LayoutInfo _layoutInfo;
|
||||
private uint _memberCount = 0;
|
||||
private bool _layoutDirty = true;
|
||||
|
||||
private readonly List<PartyFramesBar> bars;
|
||||
private LabelHud _titleLabelHud;
|
||||
|
||||
private bool Locked => !ConfigurationManager.Instance.IsConfigWindowOpened;
|
||||
|
||||
|
||||
public PartyFramesHud(PartyFramesConfig config, string displayName) : base(config, displayName)
|
||||
{
|
||||
Configs = PartyFramesConfigs.GetConfigs();
|
||||
|
||||
config.ValueChangeEvent += OnLayoutPropertyChanged;
|
||||
Configs.HealthBar.ValueChangeEvent += OnLayoutPropertyChanged;
|
||||
Configs.HealthBar.ColorsConfig.ValueChangeEvent += OnLayoutPropertyChanged;
|
||||
|
||||
bars = new List<PartyFramesBar>(MaxMemberCount);
|
||||
for (int i = 0; i < bars.Capacity; i++)
|
||||
{
|
||||
PartyFramesBar bar = new PartyFramesBar("DelvUI_partyFramesBar" + i, Configs);
|
||||
bar.OpenContextMenuEvent += OnOpenContextMenu;
|
||||
|
||||
bars.Add(bar);
|
||||
}
|
||||
|
||||
_titleLabelHud = new LabelHud(config.ShowPartyTitleConfig);
|
||||
|
||||
PartyManager.Instance.MembersChangedEvent += OnMembersChanged;
|
||||
UpdateBars(Vector2.Zero);
|
||||
}
|
||||
|
||||
protected override void InternalDispose()
|
||||
{
|
||||
foreach (var bar in bars)
|
||||
{
|
||||
try { bar.Dispose(); }
|
||||
catch (Exception ex) { Plugin.Logger.Error($"Error disposing PartyFramesBar: {ex.Message}"); }
|
||||
}
|
||||
bars.Clear();
|
||||
|
||||
_config.ValueChangeEvent -= OnLayoutPropertyChanged;
|
||||
Configs.HealthBar.ValueChangeEvent -= OnLayoutPropertyChanged;
|
||||
Configs.HealthBar.ColorsConfig.ValueChangeEvent -= OnLayoutPropertyChanged;
|
||||
PartyManager.Instance.MembersChangedEvent -= OnMembersChanged;
|
||||
}
|
||||
|
||||
private unsafe void OnOpenContextMenu(PartyFramesBar bar)
|
||||
{
|
||||
if (bar.Member == null || Plugin.ObjectTable.LocalPlayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (PartyManager.Instance.PartyListAddon == null || PartyManager.Instance.HudAgent == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int addonId = PartyManager.Instance.PartyListAddon->AtkUnitBase.Id;
|
||||
AgentModule.Instance()->GetAgentHUD()->OpenContextMenuFromPartyAddon(addonId, bar.Member.Index);
|
||||
}
|
||||
|
||||
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(PartyManager sender)
|
||||
{
|
||||
UpdateBars(_origin);
|
||||
}
|
||||
|
||||
public void UpdateBars(Vector2 origin)
|
||||
{
|
||||
uint memberCount = PartyManager.Instance.MemberCount;
|
||||
uint row = 0;
|
||||
uint col = 0;
|
||||
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
PartyFramesBar bar = bars[i];
|
||||
if (i >= memberCount)
|
||||
{
|
||||
bar.Visible = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// update bar
|
||||
IPartyFramesMember member = PartyManager.Instance.SortedGroupMembers.ElementAt(i);
|
||||
bar.Member = member;
|
||||
bar.Visible = true;
|
||||
|
||||
// anchor and position
|
||||
CalculateBarPosition(origin, Size, out var x, out var y);
|
||||
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
|
||||
);
|
||||
|
||||
// layout
|
||||
if (Config.FillRowsFirst)
|
||||
{
|
||||
col = col + 1;
|
||||
if (col >= _layoutInfo.TotalColCount)
|
||||
{
|
||||
col = 0;
|
||||
row = row + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
row = row + 1;
|
||||
if (row >= _layoutInfo.TotalRowCount)
|
||||
{
|
||||
row = 0;
|
||||
col = col + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 void UpdateBarsPosition(Vector2 delta)
|
||||
{
|
||||
foreach (PartyFramesBar bar in bars)
|
||||
{
|
||||
bar.Position = bar.Position + delta;
|
||||
}
|
||||
}
|
||||
|
||||
public void StopPreview()
|
||||
{
|
||||
Config.Preview = false;
|
||||
PartyManager.Instance?.UpdatePreview();
|
||||
|
||||
foreach (PartyFramesBar bar in bars)
|
||||
{
|
||||
bar.StopPreview();
|
||||
}
|
||||
}
|
||||
|
||||
protected override (List<Vector2>, List<Vector2>) ChildrenPositionsAndSizes()
|
||||
{
|
||||
return (new List<Vector2>() { Config.Position + Size / 2f }, new List<Vector2>() { Size });
|
||||
}
|
||||
|
||||
public void StopMouseover()
|
||||
{
|
||||
foreach (PartyFramesBar bar in bars)
|
||||
{
|
||||
bar.StopMouseover();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Vector2 contentStartPos = origin + Config.Position;
|
||||
uint count = PartyManager.Instance.MemberCount;
|
||||
|
||||
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 override void DrawChildren(Vector2 origin)
|
||||
{
|
||||
if (!_config.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// area bg
|
||||
if (Config.Preview)
|
||||
{
|
||||
AddDrawAction(StrataLevel.LOWEST, () =>
|
||||
{
|
||||
ImDrawListPtr 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);
|
||||
});
|
||||
}
|
||||
|
||||
uint count = PartyManager.Instance.MemberCount;
|
||||
if (count < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateLayout(origin);
|
||||
|
||||
// draw bars
|
||||
// check borders to determine the order in which the bars are drawn
|
||||
// which is necessary for grid-like party frames
|
||||
|
||||
IGameObject? target = Plugin.TargetManager.SoftTarget ?? Plugin.TargetManager.Target;
|
||||
int targetIndex = -1;
|
||||
int enmityLeaderIndex = -1;
|
||||
int enmitySecondIndex = -1;
|
||||
|
||||
List<int> raisedIndexes = new List<int>();
|
||||
List<int> cleanseIndexes = new List<int>();
|
||||
List<int> whosTalkingIndexes = new List<int>();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
IPartyFramesMember? member = bars[i].Member;
|
||||
|
||||
if (member != null)
|
||||
{
|
||||
// whos talking
|
||||
if (Configs.Icons.WhosTalking.ChangeBorders && member.WhosTalkingState != WhosTalkingState.None)
|
||||
{
|
||||
whosTalkingIndexes.Add(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// target
|
||||
if (target != null && member.ObjectId == target.GameObjectId)
|
||||
{
|
||||
targetIndex = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// cleanse
|
||||
bool cleanseCheck = true;
|
||||
if (Configs.Trackers.Cleanse.CleanseJobsOnly)
|
||||
{
|
||||
cleanseCheck = Utils.IsOnCleanseJob();
|
||||
}
|
||||
|
||||
if (Configs.Trackers.Cleanse.Enabled && Configs.Trackers.Cleanse.ChangeBorderCleanseColor && member.HasDispellableDebuff && cleanseCheck)
|
||||
{
|
||||
cleanseIndexes.Add(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// raise
|
||||
if (Configs.Trackers.Raise.Enabled && Configs.Trackers.Raise.ChangeBorderColorWhenRaised && member.RaiseTime.HasValue)
|
||||
{
|
||||
raisedIndexes.Add(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// enmity
|
||||
if (Configs.HealthBar.ColorsConfig.ShowEnmityBorderColors)
|
||||
{
|
||||
if (member.EnmityLevel == EnmityLevel.Leader)
|
||||
{
|
||||
enmityLeaderIndex = i;
|
||||
continue;
|
||||
}
|
||||
else if (Configs.HealthBar.ColorsConfig.ShowSecondEnmity && member.EnmityLevel == EnmityLevel.Second &&
|
||||
(count > 4 || !Configs.HealthBar.ColorsConfig.HideSecondEnmityInLightParties))
|
||||
{
|
||||
enmitySecondIndex = i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no special border
|
||||
AddDrawActions(bars[i].GetBarDrawActions(origin));
|
||||
}
|
||||
|
||||
// special colors for borders
|
||||
|
||||
// 2nd enmity
|
||||
if (enmitySecondIndex >= 0)
|
||||
{
|
||||
AddDrawActions(bars[enmitySecondIndex].GetBarDrawActions(origin, Configs.HealthBar.ColorsConfig.EnmitySecondBordercolor));
|
||||
}
|
||||
|
||||
// 1st enmity
|
||||
if (enmityLeaderIndex >= 0)
|
||||
{
|
||||
AddDrawActions(bars[enmityLeaderIndex].GetBarDrawActions(origin, Configs.HealthBar.ColorsConfig.EnmityLeaderBordercolor));
|
||||
}
|
||||
|
||||
// raise
|
||||
foreach (int index in raisedIndexes)
|
||||
{
|
||||
AddDrawActions(bars[index].GetBarDrawActions(origin, Configs.Trackers.Raise.BorderColor));
|
||||
}
|
||||
|
||||
// target
|
||||
if (targetIndex >= 0)
|
||||
{
|
||||
AddDrawActions(bars[targetIndex].GetBarDrawActions(origin, Configs.HealthBar.ColorsConfig.TargetBordercolor));
|
||||
}
|
||||
|
||||
// cleanseable debuff
|
||||
foreach (int index in cleanseIndexes)
|
||||
{
|
||||
AddDrawActions(bars[index].GetBarDrawActions(origin, Configs.Trackers.Cleanse.BorderColor));
|
||||
}
|
||||
|
||||
// whos talking
|
||||
foreach (int index in whosTalkingIndexes)
|
||||
{
|
||||
IPartyFramesMember? member = bars[index].Member;
|
||||
if (member != null)
|
||||
{
|
||||
AddDrawActions(bars[index].GetBarDrawActions(origin, Configs.Icons.WhosTalking.ColorForState(member.WhosTalkingState)));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddDrawActions(bars[index].GetBarDrawActions(origin));
|
||||
}
|
||||
}
|
||||
|
||||
// extra elements
|
||||
foreach (PartyFramesBar bar in bars)
|
||||
{
|
||||
AddDrawActions(bar.GetElementsDrawActions(origin));
|
||||
}
|
||||
|
||||
AddDrawAction(Config.ShowPartyTitleConfig.StrataLevel, () =>
|
||||
{
|
||||
Config.ShowPartyTitleConfig.SetText(PartyManager.Instance.PartyTitle);
|
||||
_titleLabelHud.Draw(origin + Config.Position);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#region utils
|
||||
public struct PartyFramesConfigs
|
||||
{
|
||||
public PartyFramesHealthBarsConfig HealthBar;
|
||||
public PartyFramesManaBarConfig ManaBar;
|
||||
public PartyFramesCastbarConfig CastBar;
|
||||
public PartyFramesIconsConfig Icons;
|
||||
public PartyFramesBuffsConfig Buffs;
|
||||
public PartyFramesDebuffsConfig Debuffs;
|
||||
public PartyFramesTrackersConfig Trackers;
|
||||
public PartyFramesCooldownListConfig CooldownList;
|
||||
|
||||
public PartyFramesConfigs(
|
||||
PartyFramesHealthBarsConfig healthBar,
|
||||
PartyFramesManaBarConfig manaBar,
|
||||
PartyFramesCastbarConfig castBar,
|
||||
PartyFramesIconsConfig icons,
|
||||
PartyFramesBuffsConfig buffs,
|
||||
PartyFramesDebuffsConfig debuffs,
|
||||
PartyFramesTrackersConfig trackers,
|
||||
PartyFramesCooldownListConfig cooldownList)
|
||||
{
|
||||
HealthBar = healthBar;
|
||||
ManaBar = manaBar;
|
||||
CastBar = castBar;
|
||||
Icons = icons;
|
||||
Buffs = buffs;
|
||||
Debuffs = debuffs;
|
||||
Trackers = trackers;
|
||||
CooldownList = cooldownList;
|
||||
}
|
||||
|
||||
public static PartyFramesConfigs GetConfigs()
|
||||
{
|
||||
return new PartyFramesConfigs(
|
||||
ConfigurationManager.Instance.GetConfigObject<PartyFramesHealthBarsConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<PartyFramesManaBarConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<PartyFramesCastbarConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<PartyFramesIconsConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<PartyFramesBuffsConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<PartyFramesDebuffsConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<PartyFramesTrackersConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<PartyFramesCooldownListConfig>()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user