442 lines
14 KiB
C#
442 lines
14 KiB
C#
namespace CBT.Types;
|
|
|
|
using System;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using CBT.FlyText.Animations;
|
|
using CBT.FlyText.Configuration;
|
|
using CBT.Helpers;
|
|
using CBT;
|
|
using Dalamud.Interface.Textures;
|
|
using Dalamud.Interface.Textures.TextureWraps;
|
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
|
using Dalamud.Bindings.ImGui;
|
|
using Lumina.Text.ReadOnly;
|
|
using static FFXIVClientStructs.FFXIV.Client.Game.Character.ActionEffectHandler;
|
|
|
|
/// <summary>
|
|
/// The fly text event.
|
|
/// </summary>
|
|
public unsafe partial class FlyTextEvent
|
|
{
|
|
private Vector2? flyTextSize;
|
|
private string? formattedText;
|
|
private Vector2? configuredOffset;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="FlyTextEvent"/> class.
|
|
/// </summary>
|
|
/// <param name="kind">Kind of the FlyText event.</param>
|
|
/// <param name="effects">An array of effects associated with this event.</param>
|
|
/// <param name="target">Target for the incoming FlyText event.</param>
|
|
/// <param name="source">Source of the FlyText event.</param>
|
|
/// <param name="option">Option. Unused.</param>
|
|
/// <param name="actionKind">ActionKind of the FlyText event.</param>
|
|
/// <param name="actionID">ActionID of the FlyText event.</param>
|
|
/// <param name="val1">Val1 of the FlyText event.</param>
|
|
/// <param name="val2">Val2 of the FlyText event.</param>
|
|
/// <param name="val3">Val3 of the FlyText event.</param>
|
|
/// <param name="val4">Val4 of the FlyText event.</param>
|
|
public FlyTextEvent(FlyTextKind kind, Effect[] effects, Character* target, Character* source, int option, int actionKind, int actionID, int val1, int val2, int val3, int val4)
|
|
{
|
|
this.Kind = kind;
|
|
this.Effects = effects;
|
|
this.Target = target;
|
|
this.Source = source;
|
|
this.Option = option;
|
|
this.ActionKind = actionKind;
|
|
this.ActionID = actionID;
|
|
this.Value1 = val1;
|
|
this.Value2 = val2;
|
|
this.Value3 = val3;
|
|
this.Value4 = val4;
|
|
|
|
this.Config = new FlyTextConfiguration(kind);
|
|
this.Animation = FlyTextAnimation.Create(kind);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="FlyTextEvent"/> class.
|
|
/// </summary>
|
|
public FlyTextEvent()
|
|
{
|
|
this.Effects = null!;
|
|
this.Target = null!;
|
|
this.Source = null!;
|
|
this.Option = default;
|
|
this.ActionKind = default;
|
|
this.ActionID = default;
|
|
this.Value1 = default;
|
|
this.Value2 = default;
|
|
this.Value3 = default;
|
|
this.Value4 = default;
|
|
|
|
this.Config = null!;
|
|
this.Animation = null!;
|
|
}
|
|
|
|
/// <summary>
|
|
/// gets a value indicating whether an event is expired.
|
|
/// </summary>
|
|
public bool IsExpired
|
|
=> this.Animation?.TimeElapsed > this.Animation?.Duration;
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating the world anchor of the event.
|
|
/// </summary>
|
|
public Vector2 Anchor
|
|
{
|
|
get
|
|
{
|
|
if (PluginManager.IsPlayerCharacter(this.Target))
|
|
{
|
|
Service.Configuration.Options.TryGetValue(GlobalOption.PlayerAnchor.ToString(), out var isAnchorFree);
|
|
|
|
if (isAnchorFree)
|
|
{
|
|
return Service.Configuration.FreeMoveAnchor;
|
|
}
|
|
}
|
|
|
|
return Service.GameGui.WorldToScreen(this.Target->Position, out Vector2 currentPosition) ? currentPosition : Vector2.Zero;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets offset from the configuration.
|
|
/// </summary>
|
|
public Vector2 Offset
|
|
{
|
|
get
|
|
{
|
|
if (this.configuredOffset == null)
|
|
{
|
|
this.configuredOffset = this.Config?.Offset ?? Vector2.Zero;
|
|
}
|
|
|
|
return (Vector2)this.configuredOffset;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the position of the FlyTextEvent with the animation offset applied.
|
|
/// </summary>
|
|
public Vector2 Position
|
|
=> this.Anchor + this.Offset + this.Animation.Offset;
|
|
|
|
/// <summary>
|
|
/// Gets the size of the FlyTextEvent rectangle.
|
|
/// </summary>
|
|
public Vector2 Size
|
|
{
|
|
get
|
|
{
|
|
if (this.flyTextSize != null)
|
|
{
|
|
return (Vector2)this.flyTextSize;
|
|
}
|
|
else
|
|
{
|
|
using (Service.Fonts.Push(this.Config.Font.Name, this.Config.Font.Size))
|
|
{
|
|
var textSize = ImGui.CalcTextSize(this.Text);
|
|
|
|
if (!this.Config.Icon.Enabled)
|
|
{
|
|
return textSize;
|
|
}
|
|
|
|
var iconSize = this.Config.Icon.Enabled ? this.Config.Icon.Size : Vector2.Zero;
|
|
|
|
var totalWidth = iconSize.X + textSize.X + this.Config.Icon.Offset.X;
|
|
var totalHeight = Math.Max(textSize.Y, iconSize.Y);
|
|
|
|
this.flyTextSize = new Vector2(totalWidth, totalHeight);
|
|
}
|
|
|
|
return (Vector2)this.flyTextSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the string representation of the FlyTextEvent Value1.
|
|
/// </summary>
|
|
public string Text
|
|
{
|
|
get
|
|
{
|
|
this.formattedText = this.Kind.IsStatus() ? this.Format(this.Name.ExtractText()) : this.Kind.IsMessage() ? this.Format(this.Kind.Pretty()) : this.Format(this.Value1.ToString("N0"));
|
|
|
|
return this.formattedText;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the Dalamud Texture Wrap for the ActionID.
|
|
/// </summary>
|
|
public IDalamudTextureWrap? Icon => Service.TextureProvider.GetFromGameIcon(new GameIconLookup(this.IconID)).GetWrapOrDefault();
|
|
|
|
/// <summary>
|
|
/// Gets the IconID for the provided Action.
|
|
/// </summary>
|
|
public uint IconID
|
|
{
|
|
get
|
|
{
|
|
if (this.Kind.IsStatus())
|
|
{
|
|
return Service.Sheet.GetIconForStatus(this.Value1);
|
|
}
|
|
|
|
if (this.Kind.IsOther())
|
|
{
|
|
return Service.Sheet.GetIconForItem(this.ActionID);
|
|
}
|
|
|
|
return Service.Sheet.GetIconForAction(this.ActionID);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the Name for the provided Action.
|
|
/// </summary>
|
|
public ReadOnlySeString Name
|
|
{
|
|
get
|
|
{
|
|
if (this.Kind.IsStatus())
|
|
{
|
|
return Service.Sheet.GetNameForStatus(this.Value1);
|
|
}
|
|
|
|
if (this.Kind.IsOther())
|
|
{
|
|
return Service.Sheet.GetNameForItem(this.ActionID);
|
|
}
|
|
|
|
return Service.Sheet.GetNameForAction(this.ActionID);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the damage type of an action.
|
|
/// </summary>
|
|
public DamageKind? DamageKind
|
|
=> this.Kind.InCategory(FlyTextCategory.AbilityDamage)
|
|
? (DamageKind)(this.Effects
|
|
.ToList()
|
|
.Where(effect => (ActionEventKind)effect.Type == ActionEventKind.Damage)
|
|
.FirstOrDefault(default(Effect))
|
|
.Param1 & 0xF)
|
|
: null;
|
|
|
|
/// <summary>
|
|
/// Gets the positional state for an action.
|
|
/// </summary>
|
|
public PositionalState PositionalState
|
|
=> this.Kind.InCategory(FlyTextCategory.AbilityDamage)
|
|
? PositionalManager
|
|
.PositionalSucceeded(this.ActionID, this.Effects.ToList().Where(effect => (ActionEventKind)effect.Type == ActionEventKind.Damage).FirstOrDefault().Param2)
|
|
: PositionalState.None;
|
|
|
|
/// <summary>
|
|
/// Gets the color for a flytext event.
|
|
/// </summary>
|
|
public Vector4 Color
|
|
{
|
|
get
|
|
{
|
|
if (this.Config.Positionals)
|
|
{
|
|
return this.PositionalState switch
|
|
{
|
|
PositionalState.None => this.Config.Font.Color,
|
|
PositionalState.Success => this.Config.Font.ColorSuccess,
|
|
PositionalState.Failed => this.Config.Font.ColorFailed,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(this.PositionalState), this.PositionalState, null),
|
|
};
|
|
}
|
|
else
|
|
{
|
|
return this.Config.Font.Color;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the fly text kind.
|
|
/// </summary>
|
|
public FlyTextKind Kind { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the FlyTextConfiguration.
|
|
/// </summary>
|
|
public FlyTextConfiguration Config { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the FlyTextAnimation.
|
|
/// </summary>
|
|
public FlyTextAnimation Animation { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Effects associated with the event.
|
|
/// </summary>
|
|
public Effect[] Effects { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Target from the original event.
|
|
/// </summary>
|
|
public Character* Target { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Source from the original event.
|
|
/// </summary>
|
|
public Character* Source { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Option from the original event.
|
|
/// </summary>
|
|
public int Option { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the ActionKind from the original event.
|
|
/// </summary>
|
|
public int ActionKind { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the ActionID from the original event.
|
|
/// </summary>
|
|
public int ActionID { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Value1 from the original event.
|
|
/// </summary>
|
|
public int Value1 { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Value2 from the original event.
|
|
/// </summary>
|
|
public int Value2 { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Value3 from the original event.
|
|
/// </summary>
|
|
public int Value3 { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Value4 from the original event.
|
|
/// </summary>
|
|
public int Value4 { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the source ID from the original event.
|
|
/// </summary>
|
|
public uint SourceID { get; set; }
|
|
|
|
/// <summary>
|
|
/// Update applies state changes to the FlyText event.
|
|
/// </summary>
|
|
/// <param name="timeElapsed">Time since the last frame.</param>
|
|
public void Update(float timeElapsed)
|
|
{
|
|
if (this.Animation != null)
|
|
{
|
|
this.Animation.TimeElapsed += timeElapsed / 1000;
|
|
this.Animation.Apply(this, timeElapsed / 1000);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset the state.
|
|
/// </summary>
|
|
public void Reset()
|
|
{
|
|
this.Kind = FlyTextKind.None;
|
|
this.Effects = null!;
|
|
this.Target = null!;
|
|
this.Source = null!;
|
|
this.Option = default;
|
|
this.ActionKind = default;
|
|
this.ActionID = default;
|
|
this.Value1 = default;
|
|
this.Value2 = default;
|
|
this.Value3 = default;
|
|
this.Value4 = default;
|
|
|
|
this.SourceID = default;
|
|
|
|
this.Config = null!;
|
|
this.Animation = null!;
|
|
|
|
this.flyTextSize = default;
|
|
this.formattedText = default;
|
|
this.configuredOffset = default;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Give life.
|
|
/// </summary>
|
|
/// <param name="kind">Kind of the FlyText event.</param>
|
|
/// <param name="effects">An array of effects associated with this event.</param>
|
|
/// <param name="sourceID">The source ID of the caster.</param>
|
|
/// <param name="target">Target for the incoming FlyText event.</param>
|
|
/// <param name="source">Source of the FlyText event.</param>
|
|
/// <param name="option">Option. Unused.</param>
|
|
/// <param name="actionKind">ActionKind of the FlyText event.</param>
|
|
/// <param name="actionID">ActionID of the FlyText event.</param>
|
|
/// <param name="val1">Val1 of the FlyText event.</param>
|
|
/// <param name="val2">Val2 of the FlyText event.</param>
|
|
/// <param name="val3">Val3 of the FlyText event.</param>
|
|
/// <param name="val4">Val4 of the FlyText event.</param>
|
|
public void Hydrate(FlyTextKind kind, Effect[] effects, uint sourceID, Character* target, Character* source, int option, int actionKind, int actionID, int val1, int val2, int val3, int val4)
|
|
{
|
|
this.Kind = kind;
|
|
this.Effects = effects;
|
|
this.Target = target;
|
|
this.Source = source;
|
|
this.Option = option;
|
|
this.ActionKind = actionKind;
|
|
this.ActionID = actionID;
|
|
this.Value1 = val1;
|
|
this.Value2 = val2;
|
|
this.Value3 = val3;
|
|
this.Value4 = val4;
|
|
|
|
// This is a special case for HP Regen attribution.
|
|
this.SourceID = sourceID;
|
|
|
|
var eventConfig = PluginManager.GetConfigForEvent(kind, source, target);
|
|
this.Config = new FlyTextConfiguration(eventConfig ?? Service.Configuration.FlyTextKinds[kind]);
|
|
this.Animation = FlyTextAnimation.Create(kind, this.Config);
|
|
}
|
|
|
|
// TODO @cultbaus: I want to use string formatting and text tags instead of this, but for now this can be a placeholder.
|
|
private string Format(string originalValue)
|
|
{
|
|
var outMessage = originalValue;
|
|
|
|
if (this.Config != null)
|
|
{
|
|
if (!this.Config.Message.Format)
|
|
{
|
|
return outMessage;
|
|
}
|
|
|
|
if (this.Config.Message.Prefix != string.Empty)
|
|
{
|
|
outMessage = $"{this.Config.Message.Prefix} {outMessage}";
|
|
}
|
|
|
|
if (this.Config.Message.Suffix != string.Empty)
|
|
{
|
|
outMessage = $"{outMessage} {this.Config.Message.Prefix}";
|
|
}
|
|
|
|
return outMessage;
|
|
}
|
|
|
|
return originalValue;
|
|
}
|
|
} |