v1.0.3.0: Alliance frames fixes, PvP support, shared hotbar persistence, config save on teleport
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -725,7 +725,7 @@ namespace HSUI.Interface.GeneralElements
|
||||
displaySlot->Set(slotType, id);
|
||||
displaySlot->LoadIconId();
|
||||
}
|
||||
module->SetAndSaveSlot(barId, (uint)idx, slotType, id);
|
||||
ActionBarsManager.SetAndSaveSlotInternal(module, barId, (uint)idx, slotType, id, displaySlot);
|
||||
try { dm->CancelDragDrop(true, true); }
|
||||
catch (Exception ex2) { Plugin.Logger.Warning($"[HSUI DragDrop] CancelDragDrop: {ex2.Message}"); }
|
||||
|
||||
|
||||
@@ -244,6 +244,11 @@ namespace HSUI.Interface
|
||||
if (partyFrames?.Enabled == true)
|
||||
AddElements(ElementKind.PartyList);
|
||||
|
||||
var alliance1 = ConfigurationManager.Instance?.GetConfigObject<AllianceFrames1Config>();
|
||||
var alliance2 = ConfigurationManager.Instance?.GetConfigObject<AllianceFrames2Config>();
|
||||
if ((alliance1?.Enabled == true || alliance2?.Enabled == true))
|
||||
AddElements(ElementKind.AllianceList1, ElementKind.AllianceList2);
|
||||
|
||||
var enemyList = ConfigurationManager.Instance?.GetConfigObject<EnemyListConfig>();
|
||||
if (enemyList?.Enabled == true)
|
||||
AddElements(ElementKind.EnemyList);
|
||||
|
||||
@@ -247,6 +247,16 @@ namespace HSUI.Interface
|
||||
_hudElements.Add(partyFramesConfig, partyFramesHud);
|
||||
_hudElementsWithPreview.Add(partyFramesHud);
|
||||
|
||||
var allianceFrames1Config = ConfigurationManager.Instance.GetConfigObject<AllianceFrames1Config>();
|
||||
var allianceFrames1Hud = new AllianceFramesHud(allianceFrames1Config, "Alliance Frames 1");
|
||||
_hudElements.Add(allianceFrames1Config, allianceFrames1Hud);
|
||||
_hudElementsWithPreview.Add(allianceFrames1Hud);
|
||||
|
||||
var allianceFrames2Config = ConfigurationManager.Instance.GetConfigObject<AllianceFrames2Config>();
|
||||
var allianceFrames2Hud = new AllianceFramesHud(allianceFrames2Config, "Alliance Frames 2");
|
||||
_hudElements.Add(allianceFrames2Config, allianceFrames2Hud);
|
||||
_hudElementsWithPreview.Add(allianceFrames2Hud);
|
||||
|
||||
var enemyListConfig = ConfigurationManager.Instance.GetConfigObject<EnemyListConfig>();
|
||||
var enemyListHud = new EnemyListHud(enemyListConfig, "Enemy List");
|
||||
_hudElements.Add(enemyListConfig, enemyListHud);
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
using HSUI.Config;
|
||||
using HSUI.Config.Attributes;
|
||||
using HSUI.Enums;
|
||||
using HSUI.Helpers;
|
||||
using HSUI.Interface.Bars;
|
||||
using HSUI.Interface.GeneralElements;
|
||||
using HSUI.Interface.StatusEffects;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using System.Numerics;
|
||||
|
||||
namespace HSUI.Interface.Party
|
||||
{
|
||||
[Exportable(false)]
|
||||
[Disableable(false)]
|
||||
[DisableParentSettings("Position", "Anchor", "BackgroundColor", "FillColor", "HideWhenInactive", "DrawBorder", "BorderColor", "BorderThickness")]
|
||||
[Section("Alliance Frames", true)]
|
||||
[SubSection("Health Bar", 0)]
|
||||
public class AllianceFramesHealthBarsConfig : PartyFramesHealthBarsConfig
|
||||
{
|
||||
public new static AllianceFramesHealthBarsConfig DefaultConfig()
|
||||
{
|
||||
var config = new AllianceFramesHealthBarsConfig(Vector2.Zero, new(100, 22), PluginConfigColor.Empty);
|
||||
config.MouseoverAreaConfig.Enabled = false;
|
||||
config.Padding = new Vector2(2, 1);
|
||||
config.ShowLabels = false;
|
||||
config.NameLabelConfig.Enabled = false;
|
||||
config.HealthLabelConfig.Enabled = false;
|
||||
config.OrderNumberConfig.Enabled = false;
|
||||
return config;
|
||||
}
|
||||
|
||||
public AllianceFramesHealthBarsConfig(Vector2 position, Vector2 size, PluginConfigColor fillColor, BarDirection fillDirection = BarDirection.Right)
|
||||
: base(position, size, fillColor, fillDirection)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Exportable(false)]
|
||||
[Section("Alliance Frames", true)]
|
||||
[SubSection("Mana Bar", 0)]
|
||||
public class AllianceFramesManaBarConfig : PartyFramesManaBarConfig
|
||||
{
|
||||
public new static AllianceFramesManaBarConfig DefaultConfig()
|
||||
{
|
||||
var config = new AllianceFramesManaBarConfig(new Vector2(0, 0), new Vector2(100, 6));
|
||||
config.Enabled = false;
|
||||
return config;
|
||||
}
|
||||
public AllianceFramesManaBarConfig(Vector2 position, Vector2 size) : base(position, size) { }
|
||||
}
|
||||
|
||||
[Exportable(false)]
|
||||
[Section("Alliance Frames", true)]
|
||||
[SubSection("Castbar", 0)]
|
||||
public class AllianceFramesCastbarConfig : PartyFramesCastbarConfig
|
||||
{
|
||||
public new static AllianceFramesCastbarConfig DefaultConfig()
|
||||
{
|
||||
var size = new Vector2(102, 10);
|
||||
var pos = new Vector2(-1, 0);
|
||||
var castNameConfig = new LabelConfig(new Vector2(5, 0), "", DrawAnchor.Left, DrawAnchor.Left);
|
||||
var castTimeConfig = new NumericLabelConfig(new Vector2(-5, 0), "", DrawAnchor.Right, DrawAnchor.Right);
|
||||
castTimeConfig.Enabled = false;
|
||||
castTimeConfig.NumberFormat = 1;
|
||||
var config = new AllianceFramesCastbarConfig(pos, size, castNameConfig, castTimeConfig);
|
||||
config.Enabled = false;
|
||||
return config;
|
||||
}
|
||||
public AllianceFramesCastbarConfig(Vector2 position, Vector2 size, LabelConfig castNameConfig, NumericLabelConfig castTimeConfig)
|
||||
: base(position, size, castNameConfig, castTimeConfig) { }
|
||||
}
|
||||
|
||||
[Disableable(false)]
|
||||
[Exportable(false)]
|
||||
[Section("Alliance Frames", true)]
|
||||
[SubSection("Icons", 0)]
|
||||
public class AllianceFramesIconsConfig : PartyFramesIconsConfig
|
||||
{
|
||||
public new static AllianceFramesIconsConfig DefaultConfig()
|
||||
{
|
||||
var config = new AllianceFramesIconsConfig();
|
||||
config.Role.Enabled = false;
|
||||
config.Sign.Enabled = false;
|
||||
config.Leader.Enabled = false;
|
||||
config.PlayerStatus.Enabled = false;
|
||||
config.ReadyCheckStatus.Icon.Enabled = false;
|
||||
config.WhosTalking.Enabled = false;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
[Exportable(false)]
|
||||
[Section("Alliance Frames", true)]
|
||||
[SubSection("Buffs", 0)]
|
||||
public class AllianceFramesBuffsConfig : PartyFramesBuffsConfig
|
||||
{
|
||||
public new static AllianceFramesBuffsConfig DefaultConfig()
|
||||
{
|
||||
var durationConfig = new LabelConfig(new Vector2(0, -4), "", DrawAnchor.Bottom, DrawAnchor.Center);
|
||||
var stacksConfig = new LabelConfig(new Vector2(-3, 4), "", DrawAnchor.TopRight, DrawAnchor.Center);
|
||||
stacksConfig.Color = new(Vector4.UnitW);
|
||||
stacksConfig.OutlineColor = new(Vector4.One);
|
||||
var iconConfig = new StatusEffectIconConfig(durationConfig, stacksConfig);
|
||||
iconConfig.DispellableBorderConfig.Enabled = false;
|
||||
iconConfig.Size = new Vector2(24, 24);
|
||||
var pos = new Vector2(-2, 2);
|
||||
var size = new Vector2(iconConfig.Size.X * 4 + 6, iconConfig.Size.Y);
|
||||
var config = new AllianceFramesBuffsConfig(DrawAnchor.TopRight, pos, size, true, false, false, GrowthDirections.Left | GrowthDirections.Down, iconConfig);
|
||||
config.Limit = 4;
|
||||
config.Enabled = false;
|
||||
return config;
|
||||
}
|
||||
public AllianceFramesBuffsConfig(DrawAnchor anchor, Vector2 position, Vector2 size, bool showBuffs, bool showDebuffs, bool showPermanentEffects,
|
||||
GrowthDirections growthDirections, StatusEffectIconConfig iconConfig)
|
||||
: base(anchor, position, size, showBuffs, showDebuffs, showPermanentEffects, growthDirections, iconConfig) { }
|
||||
}
|
||||
|
||||
[Exportable(false)]
|
||||
[Section("Alliance Frames", true)]
|
||||
[SubSection("Debuffs", 0)]
|
||||
public class AllianceFramesDebuffsConfig : PartyFramesDebuffsConfig
|
||||
{
|
||||
public new static AllianceFramesDebuffsConfig DefaultConfig()
|
||||
{
|
||||
var durationConfig = new LabelConfig(new Vector2(0, -4), "", DrawAnchor.Bottom, DrawAnchor.Center);
|
||||
var stacksConfig = new LabelConfig(new Vector2(-3, 4), "", DrawAnchor.TopRight, DrawAnchor.Center);
|
||||
stacksConfig.Color = new(Vector4.UnitW);
|
||||
stacksConfig.OutlineColor = new(Vector4.One);
|
||||
var iconConfig = new StatusEffectIconConfig(durationConfig, stacksConfig);
|
||||
iconConfig.Size = new Vector2(20, 20);
|
||||
iconConfig.DurationLabelConfig.Enabled = false;
|
||||
iconConfig.StacksLabelConfig.Enabled = false;
|
||||
var pos = new Vector2(-2, -2);
|
||||
var size = new Vector2(iconConfig.Size.X * 4 + 4, iconConfig.Size.Y);
|
||||
var config = new AllianceFramesDebuffsConfig(DrawAnchor.BottomRight, pos, size, false, true, false, GrowthDirections.Left | GrowthDirections.Up, iconConfig);
|
||||
config.Limit = 4;
|
||||
return config;
|
||||
}
|
||||
public AllianceFramesDebuffsConfig(DrawAnchor anchor, Vector2 position, Vector2 size, bool showBuffs, bool showDebuffs, bool showPermanentEffects,
|
||||
GrowthDirections growthDirections, StatusEffectIconConfig iconConfig)
|
||||
: base(anchor, position, size, showBuffs, showDebuffs, showPermanentEffects, growthDirections, iconConfig) { }
|
||||
}
|
||||
|
||||
[Disableable(false)]
|
||||
[Exportable(false)]
|
||||
[Section("Alliance Frames", true)]
|
||||
[SubSection("Trackers", 0)]
|
||||
public class AllianceFramesTrackersConfig : PartyFramesTrackersConfig
|
||||
{
|
||||
public new static AllianceFramesTrackersConfig DefaultConfig()
|
||||
{
|
||||
var config = new AllianceFramesTrackersConfig();
|
||||
config.Raise.Enabled = false;
|
||||
config.Raise.Icon.Enabled = false;
|
||||
config.Invuln.Enabled = false;
|
||||
config.Invuln.Icon.Enabled = false;
|
||||
config.Cleanse.Enabled = false;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
[Exportable(false)]
|
||||
[Section("Alliance Frames", true)]
|
||||
[SubSection("Cooldowns", 0)]
|
||||
public class AllianceFramesCooldownListConfig : PartyFramesCooldownListConfig
|
||||
{
|
||||
public new static AllianceFramesCooldownListConfig DefaultConfig()
|
||||
{
|
||||
var config = new AllianceFramesCooldownListConfig();
|
||||
config.Position = new Vector2(-2, 0);
|
||||
config.Size = new Vector2(40 * 8 + 6, 40);
|
||||
config.Enabled = false;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Base config for alliance frames. Slot 0 and 1 = the two alliances the player is NOT in.</summary>
|
||||
public abstract class AllianceFramesConfigBase : MovablePluginConfigObject
|
||||
{
|
||||
public abstract int SlotIndex { get; }
|
||||
public abstract string FramesLabel { get; }
|
||||
|
||||
[Checkbox("Preview", isMonitored = true)]
|
||||
[Order(4)]
|
||||
public bool Preview = false;
|
||||
|
||||
[DragInt("Rows", spacing = true, isMonitored = true, min = 1, max = 8, velocity = 0.2f)]
|
||||
[Order(10)]
|
||||
public int Rows = 4;
|
||||
|
||||
[DragInt("Columns", isMonitored = true, min = 1, max = 8, velocity = 0.2f)]
|
||||
[Order(11)]
|
||||
public int Columns = 2;
|
||||
|
||||
[Anchor("Bars Anchor", isMonitored = true, spacing = true)]
|
||||
[Order(15)]
|
||||
public DrawAnchor BarsAnchor = DrawAnchor.TopLeft;
|
||||
|
||||
[Checkbox("Fill Rows First", isMonitored = true)]
|
||||
[Order(20)]
|
||||
public bool FillRowsFirst = true;
|
||||
|
||||
[Checkbox("Show when not in alliance raid", help = "When enabled, shows a placeholder frame outside raids so you can position it. Disable to hide frames entirely when not in a 24-player alliance raid.")]
|
||||
[Order(25)]
|
||||
public bool ShowWhenNotInRaid = false;
|
||||
|
||||
[NestedConfig("Title Label", 60)]
|
||||
public AllianceFramesTitleLabel TitleLabelConfig = new AllianceFramesTitleLabel(Vector2.Zero, "", DrawAnchor.Left, DrawAnchor.Left);
|
||||
|
||||
[NestedConfig("Visibility", 200)]
|
||||
public VisibilityConfig VisibilityConfig = new VisibilityConfig();
|
||||
}
|
||||
|
||||
[Exportable(false)]
|
||||
[DisableParentSettings("FrameAnchor", "UseJobColor", "UseRoleColor")]
|
||||
public class AllianceFramesTitleLabel : LabelConfig
|
||||
{
|
||||
public AllianceFramesTitleLabel(Vector2 position, string text, DrawAnchor frameAnchor, DrawAnchor textAnchor)
|
||||
: base(position, text, frameAnchor, textAnchor) { }
|
||||
}
|
||||
|
||||
[Exportable(false)]
|
||||
[Section("Alliance Frames", true)]
|
||||
[SubSection("Alliance Frames 1", 0)]
|
||||
public class AllianceFrames1Config : AllianceFramesConfigBase
|
||||
{
|
||||
public override int SlotIndex => 0;
|
||||
public override string FramesLabel => "Alliance Frames 1";
|
||||
|
||||
public new static AllianceFrames1Config DefaultConfig()
|
||||
{
|
||||
var config = new AllianceFrames1Config();
|
||||
config.Position = new Vector2(-ImGui.GetMainViewport().Size.X / 3 - 180, -350);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
[Exportable(false)]
|
||||
[Section("Alliance Frames", true)]
|
||||
[SubSection("Alliance Frames 2", 0)]
|
||||
public class AllianceFrames2Config : AllianceFramesConfigBase
|
||||
{
|
||||
public override int SlotIndex => 1;
|
||||
public override string FramesLabel => "Alliance Frames 2";
|
||||
|
||||
public new static AllianceFrames2Config DefaultConfig()
|
||||
{
|
||||
var config = new AllianceFrames2Config();
|
||||
config.Position = new Vector2(-ImGui.GetMainViewport().Size.X / 3 - 180, -230);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
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<PartyFramesBar> 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<PartyFramesBar>(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<Vector2>, List<Vector2>) ChildrenPositionsAndSizes()
|
||||
{
|
||||
return (new List<Vector2>() { Config.Position + Size / 2f }, new List<Vector2>() { 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using HSUI.Helpers;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Group;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace HSUI.Interface.Party
|
||||
{
|
||||
public delegate void AllianceMembersChangedEventHandler(AllianceManager sender);
|
||||
|
||||
/// <summary>
|
||||
/// Provides member lists for Alliance A, B, and C when in 24-player raid content.
|
||||
/// Only populated when CrossRealm has 3 groups (alliance raid).
|
||||
/// </summary>
|
||||
public unsafe class AllianceManager : IDisposable
|
||||
{
|
||||
public static AllianceManager Instance { get; private set; } = null!;
|
||||
|
||||
private readonly List<IPartyFramesMember>[] _allianceMembers = new List<IPartyFramesMember>[3];
|
||||
private int[] _lastMemberCounts = new int[3];
|
||||
private bool _wasInAllianceRaid = false;
|
||||
|
||||
private AllianceManager()
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
_allianceMembers[i] = new List<IPartyFramesMember>();
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
Instance = new AllianceManager();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
_allianceMembers[i].Clear();
|
||||
Instance = null!;
|
||||
}
|
||||
|
||||
/// <summary>Internal alliance index (0, 1, or 2) containing the local player, or -1 if not in alliance raid / unknown.</summary>
|
||||
public int PlayerAllianceIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
var player = Plugin.ObjectTable?.LocalPlayer;
|
||||
if (player == null) return -1;
|
||||
uint playerEntityId = player.EntityId;
|
||||
ulong playerObjectId = (ulong)player.GameObjectId;
|
||||
|
||||
// Primary: Check our populated member lists first - most reliable (GroupManager/CrossRealm ordering may differ)
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
foreach (var m in _allianceMembers[i])
|
||||
{
|
||||
if (m.ObjectId == playerEntityId || (ulong)m.ObjectId == playerObjectId) return i;
|
||||
if (m.Character != null && m.Character.Address == player.Address) return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: InfoProxyCrossRealm.LocalPlayerGroupIndex (when in duty finder / cross-world before instance)
|
||||
var info = InfoProxyCrossRealm.Instance();
|
||||
if (info != null && info->IsInAllianceRaid && info->GroupCount >= 3)
|
||||
{
|
||||
byte localIdx = info->LocalPlayerGroupIndex;
|
||||
if (localIdx < 3) return localIdx;
|
||||
}
|
||||
|
||||
// Fallback: CrossRealm iteration
|
||||
if (info != null && info->IsInAllianceRaid && info->GroupCount >= 3)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var group = info->CrossRealmGroups[i];
|
||||
for (int j = 0; j < group.GroupMemberCount; j++)
|
||||
{
|
||||
var member = group.GroupMembers[j];
|
||||
if (member.EntityId == playerEntityId) return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback 3: Match any of our party members - we're in the same alliance as them
|
||||
var partyList = Plugin.PartyList;
|
||||
if (partyList != null)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
foreach (var m in _allianceMembers[i])
|
||||
{
|
||||
foreach (var pm in partyList)
|
||||
{
|
||||
if (pm != null && pm.EntityId == m.ObjectId)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: GroupManager - player is in MainGroup._partyMembers (their 8-man).
|
||||
// Group layout: 0,1 = other alliances from _allianceMembers; 2 = main party from _partyMembers.
|
||||
var gm = GroupManager.Instance();
|
||||
if (gm != null && gm->MainGroup.IsAlliance)
|
||||
{
|
||||
ref var mainGroup = ref gm->MainGroup;
|
||||
if (mainGroup.GetPartyMemberByEntityId(playerEntityId) != null)
|
||||
{
|
||||
return 2; // Player in main party = group 2 (our alliance)
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Slot 0 = first other alliance, slot 1 = second. Excludes the player's alliance.</summary>
|
||||
public IReadOnlyList<IPartyFramesMember> GetMembersForOtherAlliance(int slotIndex)
|
||||
{
|
||||
if (slotIndex < 0 || slotIndex > 1) return Array.Empty<IPartyFramesMember>();
|
||||
if (!TryGetAllianceIndexForSlot(slotIndex, out int allianceIdx)) return Array.Empty<IPartyFramesMember>();
|
||||
return _allianceMembers[allianceIdx].AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>Gets the alliance index (0=A, 1=B, 2=C) for the given slot. Returns false when not in raid.</summary>
|
||||
public bool TryGetAllianceIndexForSlot(int slotIndex, out int allianceIndex)
|
||||
{
|
||||
allianceIndex = -1;
|
||||
if (slotIndex < 0 || slotIndex > 1) return false;
|
||||
int playerIdx = PlayerAllianceIndex;
|
||||
int idx = 0;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (i == playerIdx) continue;
|
||||
if (idx == slotIndex)
|
||||
{
|
||||
allianceIndex = i;
|
||||
return true;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Gets the alliance letter (A/B/C) for an internal group index. Uses member EntityId matching to CrossRealm when GroupManager/CrossRealm use different orderings.</summary>
|
||||
public bool TryGetAllianceLetter(int allianceIndex, out string letter)
|
||||
{
|
||||
letter = "";
|
||||
if (allianceIndex < 0 || allianceIndex > 2) return false;
|
||||
|
||||
var info = InfoProxyCrossRealm.Instance();
|
||||
if (info != null && info->GroupCount >= 3)
|
||||
{
|
||||
// Get first member EntityId from our group (works for both GroupManager and CrossRealm data)
|
||||
uint matchEntityId = 0;
|
||||
if (_allianceMembers[allianceIndex].Count > 0)
|
||||
matchEntityId = _allianceMembers[allianceIndex][0].ObjectId;
|
||||
|
||||
// Find which CrossRealm group contains this member - they may use different ordering than GroupManager
|
||||
for (int crIdx = 0; crIdx < 3 && matchEntityId != 0; crIdx++)
|
||||
{
|
||||
var crGroup = info->CrossRealmGroups[crIdx];
|
||||
for (int j = 0; j < crGroup.GroupMemberCount; j++)
|
||||
{
|
||||
if (crGroup.GroupMembers[j].EntityId == matchEntityId)
|
||||
{
|
||||
byte displayIdx = crGroup.GroupMembers[j].GroupIndex;
|
||||
if (displayIdx < 3)
|
||||
{
|
||||
letter = ((char)('A' + displayIdx)).ToString();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Direct lookup when CrossRealm index matches ours (e.g. both use same ordering)
|
||||
var group = info->CrossRealmGroups[allianceIndex];
|
||||
if (group.GroupMemberCount > 0)
|
||||
{
|
||||
byte displayIdx = group.GroupMembers[0].GroupIndex;
|
||||
if (displayIdx < 3)
|
||||
{
|
||||
letter = ((char)('A' + displayIdx)).ToString();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// GetGroupIndex(displayLetter) returns internal index - find which letter maps to our index
|
||||
for (byte d = 0; d < 3; d++)
|
||||
{
|
||||
if (InfoProxyCrossRealm.GetGroupIndex(d) == allianceIndex)
|
||||
{
|
||||
letter = ((char)('A' + d)).ToString();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback when no CrossRealm data (PvP, in-instance)
|
||||
letter = ((char)('A' + allianceIndex)).ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>True when in 24-player content with 3 alliances (PvE) or PvP Frontlines/Rival Wings.</summary>
|
||||
public bool IsInAllianceRaid
|
||||
{
|
||||
get
|
||||
{
|
||||
var gm = GroupManager.Instance();
|
||||
if (gm != null)
|
||||
{
|
||||
// GroupManager: IsAlliance = 3x8 (alliance raids, Frontlines), IsSmallGroupAlliance = 6x4 (Rival Wings)
|
||||
if (gm->MainGroup.IsAlliance || gm->MainGroup.IsSmallGroupAlliance)
|
||||
return true;
|
||||
}
|
||||
var info = InfoProxyCrossRealm.Instance();
|
||||
if (info == null) return false;
|
||||
if (info->IsInAllianceRaid) return true;
|
||||
return info->IsCrossRealm && info->GroupCount >= 3;
|
||||
}
|
||||
}
|
||||
|
||||
public event AllianceMembersChangedEventHandler? MembersChangedEvent;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var gm = GroupManager.Instance();
|
||||
// Use GroupManager for PvE alliance raids and PvP (Frontlines, Rival Wings) - CrossRealm is not populated in PvP
|
||||
bool useGroupManager = gm != null && (gm->MainGroup.IsAlliance || gm->MainGroup.IsSmallGroupAlliance);
|
||||
|
||||
if (useGroupManager)
|
||||
{
|
||||
UpdateFromGroupManager(gm);
|
||||
return;
|
||||
}
|
||||
|
||||
var info = InfoProxyCrossRealm.Instance();
|
||||
if (info == null || !info->IsInAllianceRaid || info->GroupCount < 3)
|
||||
{
|
||||
if (_wasInAllianceRaid)
|
||||
{
|
||||
_wasInAllianceRaid = false;
|
||||
for (int i = 0; i < 3; i++)
|
||||
_allianceMembers[i].Clear();
|
||||
MembersChangedEvent?.Invoke(this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_wasInAllianceRaid = true;
|
||||
UpdateFromCrossRealm(info);
|
||||
}
|
||||
|
||||
private void UpdateFromGroupManager(GroupManager* gm)
|
||||
{
|
||||
_wasInAllianceRaid = true;
|
||||
bool anyChanged = false;
|
||||
ref var mainGroup = ref gm->MainGroup;
|
||||
|
||||
for (int allianceIdx = 0; allianceIdx < 3; allianceIdx++)
|
||||
{
|
||||
int count = 0;
|
||||
var list = new List<IPartyFramesMember>();
|
||||
for (int slot = 0; slot < 8; slot++)
|
||||
{
|
||||
var pm = mainGroup.GetAllianceMemberByGroupAndIndex(allianceIdx, slot);
|
||||
if (pm == null || pm->EntityId == 0) continue;
|
||||
var pfMember = new PartyFramesMember(
|
||||
pm->EntityId,
|
||||
count,
|
||||
count,
|
||||
EnmityLevel.Last,
|
||||
PartyMemberStatus.None,
|
||||
ReadyCheckStatus.None,
|
||||
false,
|
||||
false
|
||||
);
|
||||
pfMember.Update(EnmityLevel.Last, PartyMemberStatus.None, ReadyCheckStatus.None, false, pm->ClassJob);
|
||||
list.Add(pfMember);
|
||||
count++;
|
||||
}
|
||||
if (count != _lastMemberCounts[allianceIdx])
|
||||
{
|
||||
_allianceMembers[allianceIdx].Clear();
|
||||
_allianceMembers[allianceIdx].AddRange(list);
|
||||
_lastMemberCounts[allianceIdx] = count;
|
||||
anyChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int ourIdx = 0;
|
||||
for (int slot = 0; slot < 8 && ourIdx < _allianceMembers[allianceIdx].Count; slot++)
|
||||
{
|
||||
var pm = mainGroup.GetAllianceMemberByGroupAndIndex(allianceIdx, slot);
|
||||
if (pm == null || pm->EntityId == 0) continue;
|
||||
if (_allianceMembers[allianceIdx][ourIdx] is PartyFramesMember pfMember)
|
||||
pfMember.Update(EnmityLevel.Last, PartyMemberStatus.None, ReadyCheckStatus.None, false, pm->ClassJob);
|
||||
ourIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (anyChanged)
|
||||
MembersChangedEvent?.Invoke(this);
|
||||
}
|
||||
|
||||
/// <summary>Dumps alliance detection and letter-mapping debug info to the log. Run /hsui debug alliance while in an alliance raid.</summary>
|
||||
public static void DumpAllianceDebugToLog()
|
||||
{
|
||||
var inst = Instance;
|
||||
if (inst == null) { Plugin.Logger.Information("[HSUI Alliance DBG] AllianceManager not initialized."); return; }
|
||||
|
||||
var player = Plugin.ObjectTable?.LocalPlayer;
|
||||
uint playerEntityId = player?.EntityId ?? 0;
|
||||
string playerName = player?.Name.ToString() ?? "(null)";
|
||||
|
||||
Plugin.Logger.Information("=== HSUI Alliance Debug ===");
|
||||
Plugin.Logger.Information($"[Alliance] IsInAllianceRaid={inst.IsInAllianceRaid}");
|
||||
Plugin.Logger.Information($"[Alliance] Player: EntityId={playerEntityId} Name={playerName}");
|
||||
|
||||
var gm = GroupManager.Instance();
|
||||
bool useGM = gm != null && (gm->MainGroup.IsAlliance || gm->MainGroup.IsSmallGroupAlliance);
|
||||
Plugin.Logger.Information($"[Alliance] Data source: GroupManager={useGM} (IsAlliance={gm != null && gm->MainGroup.IsAlliance}, IsSmallGroupAlliance={gm != null && gm->MainGroup.IsSmallGroupAlliance})");
|
||||
|
||||
var info = InfoProxyCrossRealm.Instance();
|
||||
if (info != null)
|
||||
{
|
||||
Plugin.Logger.Information($"[Alliance] InfoProxyCrossRealm: GroupCount={info->GroupCount} IsInAllianceRaid={info->IsInAllianceRaid} LocalPlayerGroupIndex={info->LocalPlayerGroupIndex}");
|
||||
if (info->GroupCount >= 3)
|
||||
{
|
||||
for (byte d = 0; d < 3; d++)
|
||||
Plugin.Logger.Information($"[Alliance] GetGroupIndex({d}) => internal index {InfoProxyCrossRealm.GetGroupIndex(d)}");
|
||||
}
|
||||
}
|
||||
else
|
||||
Plugin.Logger.Information("[Alliance] InfoProxyCrossRealm: null");
|
||||
|
||||
int playerIdx = inst.PlayerAllianceIndex;
|
||||
Plugin.Logger.Information($"[Alliance] PlayerAllianceIndex={playerIdx} (internal index we treat as 'our' alliance, will exclude from display)");
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
int count = inst._allianceMembers[i].Count;
|
||||
string firstEntityId = count > 0 ? inst._allianceMembers[i][0].ObjectId.ToString() : "none";
|
||||
string firstName = count > 0 ? inst._allianceMembers[i][0].Name : "";
|
||||
bool hasPlayer = playerEntityId != 0 && inst._allianceMembers[i].Any(m => m.ObjectId == playerEntityId);
|
||||
string letter = inst.TryGetAllianceLetter(i, out string l) ? l : "?";
|
||||
Plugin.Logger.Information($"[Alliance] _allianceMembers[{i}]: count={count} firstEntityId={firstEntityId} firstName={firstName} hasPlayer={hasPlayer} resolvedLetter={letter}");
|
||||
}
|
||||
|
||||
if (info != null && info->GroupCount >= 3)
|
||||
{
|
||||
for (int crIdx = 0; crIdx < 3; crIdx++)
|
||||
{
|
||||
var grp = info->CrossRealmGroups[crIdx];
|
||||
int gc = grp.GroupMemberCount;
|
||||
string crFirstEntity = gc > 0 ? grp.GroupMembers[0].EntityId.ToString() : "none";
|
||||
string crFirstName = gc > 0 ? grp.GroupMembers[0].NameString : "";
|
||||
byte crGroupIndex = gc > 0 ? grp.GroupMembers[0].GroupIndex : (byte)255;
|
||||
char crLetter = crGroupIndex < 3 ? (char)('A' + crGroupIndex) : '?';
|
||||
Plugin.Logger.Information($"[Alliance] CrossRealmGroups[{crIdx}]: count={gc} firstEntityId={crFirstEntity} firstName={crFirstName} GroupIndex={crGroupIndex} => letter '{crLetter}'");
|
||||
}
|
||||
}
|
||||
|
||||
for (int slot = 0; slot <= 1; slot++)
|
||||
{
|
||||
if (inst.TryGetAllianceIndexForSlot(slot, out int aidx) && inst.TryGetAllianceLetter(aidx, out string let))
|
||||
Plugin.Logger.Information($"[Alliance] Display slot {slot}: allianceIndex={aidx} letter={let}");
|
||||
else
|
||||
Plugin.Logger.Information($"[Alliance] Display slot {slot}: no data");
|
||||
}
|
||||
Plugin.Logger.Information("=== End Alliance Debug ===");
|
||||
}
|
||||
|
||||
private void UpdateFromCrossRealm(InfoProxyCrossRealm* info)
|
||||
{
|
||||
bool anyChanged = false;
|
||||
for (int allianceIdx = 0; allianceIdx < 3; allianceIdx++)
|
||||
{
|
||||
var group = info->CrossRealmGroups[allianceIdx];
|
||||
int count = group.GroupMemberCount;
|
||||
|
||||
if (count != _lastMemberCounts[allianceIdx])
|
||||
{
|
||||
_allianceMembers[allianceIdx].Clear();
|
||||
_lastMemberCounts[allianceIdx] = count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
CrossRealmMember member = group.GroupMembers[i];
|
||||
var partyMember = new PartyFramesMember(
|
||||
member,
|
||||
i,
|
||||
i,
|
||||
PartyMemberStatus.None,
|
||||
ReadyCheckStatus.None,
|
||||
member.IsPartyLeader,
|
||||
false
|
||||
);
|
||||
_allianceMembers[allianceIdx].Add(partyMember);
|
||||
}
|
||||
anyChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < _allianceMembers[allianceIdx].Count; i++)
|
||||
{
|
||||
if (_allianceMembers[allianceIdx][i] is PartyFramesMember pfMember)
|
||||
{
|
||||
pfMember.Update(EnmityLevel.Last, PartyMemberStatus.None, ReadyCheckStatus.None, pfMember.IsPartyLeader, pfMember.JobId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (anyChanged)
|
||||
MembersChangedEvent?.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -576,82 +576,85 @@ namespace HSUI.Interface.Party
|
||||
}
|
||||
));
|
||||
|
||||
// name
|
||||
bool drawName = ShouldDrawName(character, showingRaise, showingInvuln);
|
||||
if (drawName)
|
||||
if (_configs.HealthBar.ShowLabels)
|
||||
{
|
||||
drawActions.Add((_configs.HealthBar.NameLabelConfig.StrataLevel, () =>
|
||||
// name
|
||||
bool drawName = ShouldDrawName(character, showingRaise, showingInvuln);
|
||||
if (drawName)
|
||||
{
|
||||
bool? playerName = null;
|
||||
if (character == null || character.ObjectKind == ObjectKind.Player)
|
||||
drawActions.Add((_configs.HealthBar.NameLabelConfig.StrataLevel, () =>
|
||||
{
|
||||
playerName = true;
|
||||
bool? playerName = null;
|
||||
if (character == null || character.ObjectKind == ObjectKind.Player)
|
||||
{
|
||||
playerName = true;
|
||||
}
|
||||
|
||||
_nameLabelHud.Draw(Position, _configs.HealthBar.Size, character, Member.Name, isPlayerName: playerName);
|
||||
}
|
||||
|
||||
_nameLabelHud.Draw(Position, _configs.HealthBar.Size, character, Member.Name, isPlayerName: playerName);
|
||||
));
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
// health label
|
||||
if (Member.MaxHP > 0)
|
||||
{
|
||||
drawActions.Add((_configs.HealthBar.HealthLabelConfig.StrataLevel, () =>
|
||||
// health label
|
||||
if (Member.MaxHP > 0)
|
||||
{
|
||||
_healthLabelHud.Draw(Position, _configs.HealthBar.Size, character, null, Member.HP, Member.MaxHP);
|
||||
drawActions.Add((_configs.HealthBar.HealthLabelConfig.StrataLevel, () =>
|
||||
{
|
||||
_healthLabelHud.Draw(Position, _configs.HealthBar.Size, character, null, Member.HP, Member.MaxHP);
|
||||
}
|
||||
));
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
// order
|
||||
if (character == null || character?.ObjectKind != ObjectKind.BattleNpc)
|
||||
{
|
||||
string str = char.ConvertFromUtf32(0xE090 + Member.Order).ToString();
|
||||
|
||||
drawActions.Add((_configs.HealthBar.OrderNumberConfig.StrataLevel, () =>
|
||||
// order
|
||||
if (character == null || character?.ObjectKind != ObjectKind.BattleNpc)
|
||||
{
|
||||
_configs.HealthBar.OrderNumberConfig.SetText(str);
|
||||
_orderLabelHud.Draw(Position, _configs.HealthBar.Size, character);
|
||||
}
|
||||
));
|
||||
}
|
||||
string str = char.ConvertFromUtf32(0xE090 + Member.Order).ToString();
|
||||
|
||||
// status
|
||||
string? statusString = StringForStatus(Member.Status);
|
||||
if (PlayerStatus.Enabled && PlayerStatus.Label.Enabled && statusString != null)
|
||||
{
|
||||
drawActions.Add((PlayerStatus.Label.StrataLevel, () =>
|
||||
drawActions.Add((_configs.HealthBar.OrderNumberConfig.StrataLevel, () =>
|
||||
{
|
||||
_configs.HealthBar.OrderNumberConfig.SetText(str);
|
||||
_orderLabelHud.Draw(Position, _configs.HealthBar.Size, character);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
// status
|
||||
string? statusString = StringForStatus(Member.Status);
|
||||
if (PlayerStatus.Enabled && PlayerStatus.Label.Enabled && statusString != null)
|
||||
{
|
||||
PlayerStatus.Label.SetText(statusString);
|
||||
_statusLabelHud.Draw(Position, _configs.HealthBar.Size);
|
||||
drawActions.Add((PlayerStatus.Label.StrataLevel, () =>
|
||||
{
|
||||
PlayerStatus.Label.SetText(statusString);
|
||||
_statusLabelHud.Draw(Position, _configs.HealthBar.Size);
|
||||
}
|
||||
));
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
// raise label
|
||||
if (showingRaise)
|
||||
{
|
||||
float duration = Math.Abs(Member.RaiseTime!.Value);
|
||||
|
||||
drawActions.Add((RaiseTracker.Icon.NumericLabel.StrataLevel, () =>
|
||||
// raise label
|
||||
if (showingRaise)
|
||||
{
|
||||
RaiseTracker.Icon.NumericLabel.SetValue(duration);
|
||||
_raiseLabelHud.Draw(Position, _configs.HealthBar.Size);
|
||||
float duration = Math.Abs(Member.RaiseTime!.Value);
|
||||
|
||||
drawActions.Add((RaiseTracker.Icon.NumericLabel.StrataLevel, () =>
|
||||
{
|
||||
RaiseTracker.Icon.NumericLabel.SetValue(duration);
|
||||
_raiseLabelHud.Draw(Position, _configs.HealthBar.Size);
|
||||
}
|
||||
));
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
// invuln label
|
||||
if (showingInvuln)
|
||||
{
|
||||
float duration = Math.Abs(Member.InvulnStatus!.InvulnTime);
|
||||
|
||||
drawActions.Add((InvulnTracker.Icon.NumericLabel.StrataLevel, () =>
|
||||
// invuln label
|
||||
if (showingInvuln)
|
||||
{
|
||||
InvulnTracker.Icon.NumericLabel.SetValue(duration);
|
||||
_invulnLabelHud.Draw(Position, _configs.HealthBar.Size);
|
||||
float duration = Math.Abs(Member.InvulnStatus!.InvulnTime);
|
||||
|
||||
drawActions.Add((InvulnTracker.Icon.NumericLabel.StrataLevel, () =>
|
||||
{
|
||||
InvulnTracker.Icon.NumericLabel.SetValue(duration);
|
||||
_invulnLabelHud.Draw(Position, _configs.HealthBar.Size);
|
||||
}
|
||||
));
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
return drawActions;
|
||||
|
||||
@@ -88,6 +88,10 @@ namespace HSUI.Interface.Party
|
||||
[Order(31)]
|
||||
public Vector2 Padding = new Vector2(0, 0);
|
||||
|
||||
[Checkbox("Show Labels", help = "Show name, health, order number, and status text on the bar.")]
|
||||
[Order(32)]
|
||||
public bool ShowLabels = true;
|
||||
|
||||
[NestedConfig("Name Label", 44)]
|
||||
public EditableLabelConfig NameLabelConfig = new EditableLabelConfig(Vector2.Zero, "[name:initials].", DrawAnchor.Center, DrawAnchor.Center);
|
||||
|
||||
|
||||
@@ -459,7 +459,19 @@ namespace HSUI.Interface.Party
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public static PartyFramesConfigs GetAllianceConfigs()
|
||||
{
|
||||
return new PartyFramesConfigs(
|
||||
ConfigurationManager.Instance.GetConfigObject<AllianceFramesHealthBarsConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<AllianceFramesManaBarConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<AllianceFramesCastbarConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<AllianceFramesIconsConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<AllianceFramesBuffsConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<AllianceFramesDebuffsConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<AllianceFramesTrackersConfig>(),
|
||||
ConfigurationManager.Instance.GetConfigObject<AllianceFramesCooldownListConfig>()
|
||||
);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user