Files
HSUI/Interface/GeneralElements/CastbarHud.cs

510 lines
18 KiB
C#

using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Utility;
using HSUI.Config;
using HSUI.Enums;
using HSUI.Helpers;
using HSUI.Interface.Bars;
using HSUI.Interface.EnemyList;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Component.GUI;
using System;
using System.Collections.Generic;
using System.Numerics;
using LuminaAction = Lumina.Excel.Sheets.Action;
using StructsBattleChara = FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara;
namespace HSUI.Interface.GeneralElements
{
public class CastbarHud : ParentAnchoredDraggableHudElement, IHudElementWithActor, IHudElementWithAnchorableParent, IHudElementWithPreview
{
private CastbarConfig Config => (CastbarConfig)_config;
private readonly LabelHud _castNameLabel;
private readonly LabelHud _castTimeLabel;
protected LastUsedCast? LastUsedCast;
public IGameObject? Actor { get; set; }
protected override bool AnchorToParent => Config is UnitFrameCastbarConfig { AnchorToUnitFrame: true };
protected override DrawAnchor ParentAnchor => Config is UnitFrameCastbarConfig config ? config.UnitFrameAnchor : DrawAnchor.Center;
public CastbarHud(CastbarConfig config, string? displayName = null) : base(config, displayName)
{
_castNameLabel = new LabelHud(config.CastNameLabel);
_castTimeLabel = new LabelHud(config.CastTimeLabel);
}
public void StopPreview()
{
Config.Preview = false;
}
protected override (List<Vector2>, List<Vector2>) ChildrenPositionsAndSizes() => (new List<Vector2> { Config.Position }, new List<Vector2> { Config.Size });
public override void DrawChildren(Vector2 origin)
{
if (!Config.Enabled)
{
return;
}
if (!Config.Preview &&
(Actor == null || Actor is not ICharacter || Actor.ObjectKind != ObjectKind.Player && Actor.ObjectKind != ObjectKind.BattleNpc))
{
return;
}
UpdateCurrentCast(out float currentCastTime, out float totalCastTime);
if (totalCastTime == 0 || currentCastTime >= totalCastTime)
{
return;
}
if (!ShouldShow() && !Config.Preview)
{
return;
}
Vector2 size = GetSize();
IDalamudTextureWrap? iconTexture = LastUsedCast?.GetIconTexture();
bool validIcon = Config.Preview ? true : iconTexture is not null;
Vector2 iconSize = Config.ShowIcon && validIcon && !Config.SeparateIcon ? new Vector2(size.Y, size.Y) : Vector2.Zero;
PluginConfigColor fillColor = GetColor();
Rect background = new(Config.Position, size, Config.BackgroundColor);
Rect progress = BarUtilities.GetFillRect(Config.Position, size, Config.FillDirection, fillColor, currentCastTime, totalCastTime);
BarHud bar = new(Config, Actor);/**/
bar.SetBackground(background);
if (Config.UseReverseFill)
{
Vector2 reverseFillSize = size - BarUtilities.GetFillDirectionOffset(progress.Size, Config.FillDirection);
Vector2 reverseFillPos = Config.FillDirection.IsInverted()
? Config.Position
: Config.Position + BarUtilities.GetFillDirectionOffset(progress.Size, Config.FillDirection);
PluginConfigColor reverseFillColor = Config.ReverseFillColor;
bar.AddForegrounds(new Rect(reverseFillPos, reverseFillSize, reverseFillColor));
}
AddExtras(bar, totalCastTime, iconTexture);
bar.AddForegrounds(progress);
Vector2 pos = origin + ParentPos();
AddDrawActions(bar.GetDrawActions(pos, Config.StrataLevel));
// icon
Vector2 startPos = Config.Position + Utils.GetAnchoredPosition(pos, size, Config.Anchor);
if (Config.ShowIcon && validIcon)
{
Vector2 finalIconPos = Config.SeparateIcon ? startPos + Config.CustomIconPosition : startPos;
Vector2 finalIconSize = Config.SeparateIcon ? Config.CustomIconSize : iconSize;
AddDrawAction(Config.StrataLevel, () =>
{
DrawHelper.DrawInWindow(ID + "_icon", finalIconPos, finalIconSize, false, (drawList) =>
{
ImGui.SetCursorPos(finalIconPos);
IDalamudTextureWrap? texture = Config.Preview ? TexturesHelper.GetTexture<LuminaAction>(3577) : iconTexture;
if (texture != null)
{
ImGui.Image(texture.Handle, finalIconSize);
}
if (Config.DrawBorder)
{
drawList.AddRect(finalIconPos, finalIconPos + finalIconSize, Config.BorderColor.Base, 0, ImDrawFlags.None, Config.BorderThickness);
}
});
});
}
// cast time
bool isTimeLeftAnchored = Config.CastTimeLabel.TextAnchor is DrawAnchor.Left or DrawAnchor.TopLeft or DrawAnchor.BottomLeft;
Vector2 timePos = Config.ShowIcon && isTimeLeftAnchored ? startPos + new Vector2(iconSize.X, 0) : startPos;
float value = Config.Preview ? 0.5f : totalCastTime - currentCastTime;
if (Config.ShowMaxCastTime)
{
string format = Config.CastTimeLabel.NumberFormat.ToString();
Config.CastTimeLabel.SetText(
value.ToString("N" + format, ConfigurationManager.Instance.ActiveCultreInfo) +
" / " +
totalCastTime.ToString("N" + format, ConfigurationManager.Instance.ActiveCultreInfo)
);
}
else
{
Config.CastTimeLabel.SetValue(value);
}
AddDrawAction(Config.CastTimeLabel.StrataLevel, () =>
{
_castTimeLabel.Draw(timePos, size, Actor);
});
// cast name
bool isNameLeftAnchored = Config.CastNameLabel.TextAnchor is DrawAnchor.Left or DrawAnchor.TopLeft or DrawAnchor.BottomLeft;
Vector2 namePos = Config.ShowIcon && isNameLeftAnchored ? startPos + new Vector2(iconSize.X, 0) : startPos;
string original = CustomCastName() ?? (LastUsedCast?.ActionText ?? "");
string castName = EncryptedStringsHelper.GetString(original).CheckForUpperCase() ?? original;
if (Config.TruncateCastName)
{
castName = TruncatedCastName(castName) ?? castName;
}
Config.CastNameLabel.SetText(Config.Preview ? "Cast Name" : castName);
AddDrawAction(Config.CastNameLabel.StrataLevel, () =>
{
_castNameLabel.Draw(namePos, size, Actor);
});
}
private unsafe void UpdateCurrentCast(out float currentCastTime, out float totalCastTime)
{
if (Config.Preview || Actor is not IBattleChara battleChara)
{
currentCastTime = Config.Preview ? 0.5f : 0f;
totalCastTime = 1f;
return;
}
float current = 0;
float total = 0;
try
{
current = battleChara.CurrentCastTime;
StructsBattleChara* chara = (StructsBattleChara*)battleChara.Address;
CastInfo* castInfo = chara->GetCastInfo();
if (castInfo != null)
{
total = castInfo->TotalCastTime;
}
}
catch
{
currentCastTime = 0;
totalCastTime = 0;
return;
}
if (!Utils.IsActorCasting(battleChara) && current <= 0)
{
currentCastTime = 0;
totalCastTime = 0;
return;
}
currentCastTime = current;
totalCastTime = total;
uint currentCastId = battleChara.CastActionId;
ActionType currentCastType = (ActionType)battleChara.CastActionType;
if (LastUsedCast == null || LastUsedCast.CastId != currentCastId || LastUsedCast.ActionType != currentCastType)
{
LastUsedCast = new LastUsedCast(currentCastId, currentCastType, battleChara.IsCastInterruptible);
}
}
private string? TruncatedCastName(string text)
{
if (text.Length <= 5)
{
return null;
}
LabelConfig castNamelabel = Config.CastNameLabel;
LabelConfig castTimeLabel = Config.CastTimeLabel;
Vector2 size;
using (FontsManager.Instance.PushFont(castNamelabel.FontID))
{
size = ImGui.CalcTextSize(text) * castNamelabel.GetFontScale();
}
float maxWidth = Config.Size.X;
if (!Config.SeparateIcon)
{
maxWidth -= Config.Size.Y;
}
if (Config.CastTimeLabel.Enabled)
{
using (FontsManager.Instance.PushFont(Config.CastTimeLabel.FontID))
{
maxWidth -= (ImGui.CalcTextSize("XX.X") * castTimeLabel.GetFontScale()).X;
}
}
if (size.X > maxWidth)
{
return TruncatedCastName(text.Substring(0, text.Length - 5) + "...");
}
return text;
}
public virtual void AddExtras(BarHud bar, float totalCastTime, IDalamudTextureWrap? iconTexture)
{
// override
}
public virtual string? CustomCastName()
{
// override
return null;
}
public virtual PluginConfigColor GetColor() => Config.FillColor;
public virtual Vector2 GetSize() => Config.Size;
public virtual bool ShouldShow() => true;
}
public class PlayerCastbarHud : CastbarHud
{
private PlayerCastbarConfig Config => (PlayerCastbarConfig)_config;
public PlayerCastbarHud(PlayerCastbarConfig config, string displayName) : base(config, displayName)
{
}
public override unsafe string? CustomCastName()
{
AddonCastBar* castBar = (AddonCastBar*)Plugin.GameGui.GetAddonByName("_CastBar", 1).Address;
if (castBar == null) { return null; }
AtkTextNode* node = castBar->GetTextNodeById(4);
if (node == null) { return null; }
return node->GetText().ExtractText();
}
public override void AddExtras(BarHud bar, float totalCastTime, IDalamudTextureWrap? iconTexture)
{
if (!Config.ShowSlideCast || Config.SlideCastTime <= 0 || Config.Preview)
{
return;
}
Rect slideCast;
if (Config.FillDirection.IsHorizontal())
{
float slideCastWidth = Math.Min(Config.Size.X, Config.SlideCastTime / 1000f * Config.Size.X / totalCastTime);
Vector2 size = new(slideCastWidth, Config.Size.Y);
slideCast = new(Config.Position + Config.Size - size, size, Config.SlideCastColor);
if (Config.FillDirection is BarDirection.Left)
{
bool validIcon = iconTexture is not null;
Vector2 iconSize = Config.ShowIcon && validIcon ? new Vector2(Config.Size.Y, Config.Size.Y) : Vector2.Zero;
slideCast = Config.ShowIcon ? new Rect(Config.Position, size + new Vector2(iconSize.X, 0), Config.SlideCastColor) : new Rect(Config.Position, size, Config.SlideCastColor);
}
}
else
{
float slideCastHeight = Math.Min(Config.Size.Y, Config.SlideCastTime / 1000f * Config.Size.Y / totalCastTime);
Vector2 size = new(Config.Size.X, slideCastHeight);
slideCast = new(Config.Position + Config.Size - size, size, Config.SlideCastColor);
if (Config.FillDirection is BarDirection.Up)
{
slideCast = new(Config.Position, size, Config.SlideCastColor);
}
}
bar.AddForegrounds(slideCast);
}
public override PluginConfigColor GetColor()
{
if (!Config.UseJobColor || Actor is not ICharacter)
{
return Config.FillColor;
}
ICharacter? chara = (ICharacter)Actor;
PluginConfigColor? color = GlobalColors.Instance.ColorForJobId(chara.ClassJob.RowId);
return color ?? Config.FillColor;
}
}
public class TargetCastbarHud : CastbarHud
{
private TargetCastbarConfig Config => (TargetCastbarConfig)_config;
public TargetCastbarHud(TargetCastbarConfig config, string? displayName = null) : base(config, displayName)
{
}
public override PluginConfigColor GetColor()
{
if (Config.ShowInterruptableColor && LastUsedCast?.Interruptible == true)
{
return Config.InterruptableColor;
}
if (!Config.UseColorForDamageTypes)
{
return Config.FillColor;
}
if (LastUsedCast != null)
{
switch (LastUsedCast.DamageType)
{
case DamageType.Physical:
case DamageType.Blunt:
case DamageType.Slashing:
case DamageType.Piercing:
return Config.PhysicalDamageColor;
case DamageType.Magic:
return Config.MagicalDamageColor;
case DamageType.Darkness:
return Config.DarknessDamageColor;
}
}
return Config.FillColor;
}
public override unsafe bool ShouldShow()
{
bool? targetCasting = Utils.IsTargetCasting();
if (targetCasting.HasValue)
{
return targetCasting.Value;
}
return true;
}
}
public class FocusTargetCastbarHud : TargetCastbarHud
{
private TargetCastbarConfig Config => (TargetCastbarConfig)_config;
public FocusTargetCastbarHud(TargetCastbarConfig config, string? displayName = null) : base(config, displayName)
{
}
public override unsafe bool ShouldShow()
{
bool? focusTargetCasting = Utils.IsFocusTargetCasting();
if (focusTargetCasting.HasValue)
{
return focusTargetCasting.Value;
}
return true;
}
}
public class TargetOfTargetCastbarHud : TargetCastbarHud
{
private TargetCastbarConfig Config => (TargetCastbarConfig)_config;
public TargetOfTargetCastbarHud(TargetCastbarConfig config, string? displayName = null) : base(config, displayName)
{
}
public override unsafe bool ShouldShow()
{
IGameObject? target = Plugin.TargetManager.SoftTarget ?? Plugin.TargetManager.Target;
if (Actor == target)
{
bool? targetCasting = Utils.IsTargetCasting();
if (targetCasting.HasValue)
{
return targetCasting.Value;
}
}
IGameObject? focusTarget = Plugin.TargetManager.FocusTarget;
if (Actor == focusTarget)
{
bool? focusTargetCasting = Utils.IsFocusTargetCasting();
if (focusTargetCasting.HasValue)
{
return focusTargetCasting.Value;
}
}
return true;
}
}
public class EnemyListCastbarHud : TargetCastbarHud
{
private EnemyListCastbarConfig Config => (EnemyListCastbarConfig)_config;
public int EnemyListIndex = 0;
public EnemyListCastbarHud(EnemyListCastbarConfig config, string? displayName = null) : base(config, displayName)
{
}
public override unsafe bool ShouldShow()
{
bool? casting = Utils.IsEnemyInListCasting(EnemyListIndex);
if (casting.HasValue)
{
return casting.Value;
}
return true;
}
}
public class NameplateCastbarHud : TargetOfTargetCastbarHud
{
private NameplateCastbarConfig Config => (NameplateCastbarConfig)_config;
private Vector2 _customSize = new Vector2(0);
public Vector2 ParentSize { get; set; } = new Vector2(0);
public NameplateCastbarHud(NameplateCastbarConfig config, string? displayName = null) : base(config, displayName)
{
_customSize = Config.Size;
}
public override void DrawChildren(Vector2 origin)
{
// calculate size
float x = Config.MatchWidth ? ParentSize.X : Config.Size.X;
float y = Config.MatchHeight ? ParentSize.Y : Config.Size.Y;
_customSize = new Vector2(x, y);
// draw
base.DrawChildren(origin);
}
public override Vector2 GetSize() => _customSize;
}
}