From 288ac50a6bf19bc1a29f36ed379e328ca267f92c Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 22 Jul 2024 01:44:50 -0400 Subject: [PATCH] feat: begin adding phantom characters --- client/Plugin.cs | 4 ++ client/Ui/Viewer.cs | 34 ++++++++--- client/Util/ActorManager.cs | 117 ++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 8 deletions(-) create mode 100644 client/Util/ActorManager.cs diff --git a/client/Plugin.cs b/client/Plugin.cs index 4f560bc..42cdfad 100644 --- a/client/Plugin.cs +++ b/client/Plugin.cs @@ -2,6 +2,7 @@ using Dalamud.IoC; using Dalamud.Plugin; using Dalamud.Plugin.Services; using OrangeGuidanceTomestone.MiniPenumbra; +using OrangeGuidanceTomestone.Util; namespace OrangeGuidanceTomestone; @@ -45,6 +46,7 @@ public class Plugin : IDalamudPlugin { internal Vfx Vfx { get; } internal PluginUi Ui { get; } internal Messages Messages { get; } + internal ActorManager ActorManager { get; } internal VfxReplacer VfxReplacer { get; } internal Commands Commands { get; } internal Pinger Pinger { get; } @@ -57,6 +59,7 @@ public class Plugin : IDalamudPlugin { this.Config = this.Interface!.GetPluginConfig() as Configuration ?? new Configuration(); this.Vfx = new Vfx(this); this.Messages = new Messages(this); + this.ActorManager = new ActorManager(this); this.Ui = new PluginUi(this); this.VfxReplacer = new VfxReplacer(this); this.Commands = new Commands(this); @@ -72,6 +75,7 @@ public class Plugin : IDalamudPlugin { this.Commands.Dispose(); this.VfxReplacer.Dispose(); this.Ui.Dispose(); + this.ActorManager.Dispose(); this.Messages.Dispose(); this.Vfx.Dispose(); } diff --git a/client/Ui/Viewer.cs b/client/Ui/Viewer.cs index 8f86843..7d4a294 100644 --- a/client/Ui/Viewer.cs +++ b/client/Ui/Viewer.cs @@ -3,6 +3,7 @@ using Dalamud.Interface; using Dalamud.Interface.Utility; using ImGuiNET; using OrangeGuidanceTomestone.Helpers; +using OrangeGuidanceTomestone.Util; namespace OrangeGuidanceTomestone.Ui; @@ -11,6 +12,11 @@ internal class Viewer { internal bool Visible; + internal delegate void MessageViewDelegate(Message? message); + internal event MessageViewDelegate View; + + private Guid _lastViewed = Guid.Empty; + private int _idx; internal Viewer(Plugin plugin) { @@ -19,6 +25,11 @@ internal class Viewer { internal void Draw() { if (!this.Visible) { + if (this._lastViewed != Guid.Empty) { + this.View(null); + } + + this._lastViewed = Guid.Empty; return; } @@ -28,8 +39,8 @@ internal class Viewer { flags |= this.Plugin.Config.ClickThroughViewer ? ImGuiWindowFlags.NoInputs : ImGuiWindowFlags.None; ImGui.SetNextWindowSize(new Vector2(350, 175), ImGuiCond.FirstUseEver); ImGui.SetNextWindowBgAlpha(this.Plugin.Config.ViewerOpacity / 100.0f); + using var end = new OnDispose(ImGui.End); if (!ImGui.Begin("Messages", ref this.Visible, flags)) { - ImGui.End(); return; } @@ -47,7 +58,7 @@ internal class Viewer { ImGui.TextUnformatted("No nearby messages"); } - goto End; + return; } if (this._idx >= nearby.Count) { @@ -55,9 +66,11 @@ internal class Viewer { } if (!ImGui.BeginTable("##viewer-table", 3)) { - goto End; + return; } + using var endTable = new OnDispose(ImGui.EndTable); + ImGui.TableSetupColumn("##prev-arrow", ImGuiTableColumnFlags.WidthFixed); ImGui.TableSetupColumn("##content", ImGuiTableColumnFlags.WidthStretch); ImGui.TableSetupColumn("##next-arrow", ImGuiTableColumnFlags.WidthFixed); @@ -82,6 +95,16 @@ internal class Viewer { if (ImGui.TableSetColumnIndex(1) && this._idx > -1 && this._idx < nearby.Count) { var message = nearby[this._idx]; + if (this._lastViewed != message.Id) { + try { + this.View(message); + } catch (Exception ex) { + Plugin.Log.Error(ex, "Error in View event"); + } + } + + this._lastViewed = message.Id; + var size = ImGui.CalcTextSize(message.Text, ImGui.GetContentRegionAvail().X).Y; size += ImGui.GetStyle().ItemSpacing.Y * 2; size += ImGui.CalcTextSize("A").Y; @@ -176,10 +199,5 @@ internal class Viewer { ImGui.EndDisabled(); } } - - ImGui.EndTable(); - - End: - ImGui.End(); } } diff --git a/client/Util/ActorManager.cs b/client/Util/ActorManager.cs new file mode 100644 index 0000000..da226b1 --- /dev/null +++ b/client/Util/ActorManager.cs @@ -0,0 +1,117 @@ +using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Game.Character; +using FFXIVClientStructs.FFXIV.Client.Game.Object; + +namespace OrangeGuidanceTomestone.Util; + +internal class ActorManager : IDisposable { + private Plugin Plugin { get; } + private uint? _idx; + private readonly Queue _tasks = []; + + private enum Mode { + None, + Enable, + Disable, + Delete, + } + + internal ActorManager(Plugin plugin) { + this.Plugin = plugin; + this.Plugin.Framework.Update += this.OnFramework; + this.Plugin.Ui.Viewer.View += this.OnView; + } + + public void Dispose() { + this.Plugin.Ui.Viewer.View -= this.OnView; + this.Plugin.Framework.Update -= this.OnFramework; + } + + private unsafe void OnFramework(IFramework framework) { + if (this._idx is not { } idx) { + return; + } + + if (!this._tasks.TryPeek(out var mode)) { + return; + } + + var success = false; + + var objMan = ClientObjectManager.Instance(); + var obj = objMan->GetObjectByIndex((ushort) idx); + if (obj == null) { + Plugin.Log.Warning("actor by index was null"); + return; + } + + switch (mode) { + case Mode.Disable: { + obj->DisableDraw(); + success = true; + break; + } + case Mode.Enable: { + if (!obj->IsReadyToDraw()) { + break; + } + + obj->EnableDraw(); + success = true; + break; + } + case Mode.Delete: { + objMan->DeleteObjectByIndex((ushort) idx, 0); + this._idx = null; + success = true; + break; + } + } + + if (success) { + this._tasks.Dequeue(); + } + } + + private void OnView(Message? message) { + this.Despawn(); + + if (message != null) { + this.Spawn(message); + } + } + + internal unsafe void Spawn(Message message) { + if (this._idx != null) { + Plugin.Log.Warning("refusing to spawn more than one actor"); + return; + } + + var objMan = ClientObjectManager.Instance(); + var idx = objMan->CreateBattleCharacter(); + if (idx == 0xFFFFFFFF) { + return; + } + + this._idx = idx; + + var chara = (BattleChara*) objMan->GetObjectByIndex((ushort) idx); + + chara->Position = message.Position; + chara->Rotation = message.Yaw; + var drawData = &chara->DrawData; + drawData->CustomizeData = new CustomizeData(); + + chara->Alpha = 0.25f; + chara->SetMode(CharacterModes.AnimLock, 0); + chara->Timeline.BaseOverride = 4818; + + this._tasks.Enqueue(Mode.Enable); + } + + internal unsafe void Despawn() { + Plugin.Log.Debug("despawning actor"); + this._tasks.Enqueue(Mode.Disable); + this._tasks.Enqueue(Mode.Delete); + } +}