Initial commit: AetherBags + KamiToolKit for FC Gitea
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Game.Addon.Lifecycle;
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Enums;
|
||||
|
||||
namespace KamiToolKit.Overlay;
|
||||
|
||||
public unsafe class OverlayController : IDisposable {
|
||||
private readonly Dictionary<OverlayLayer, List<OverlayNode>> overlayNodes = [];
|
||||
private readonly Dictionary<OverlayLayer, OverlayAddonState> addonState = [];
|
||||
|
||||
private ControllerState controllerState = ControllerState.WaitForNameplate;
|
||||
|
||||
public OverlayController() {
|
||||
ClearState();
|
||||
|
||||
DalamudInterface.Instance.AddonLifecycle.RegisterListener(AddonEvent.PreFinalize, "NamePlate", OnNamePlatePreFinalize);
|
||||
|
||||
foreach (var overlayLayer in Enum.GetValues<OverlayLayer>()) {
|
||||
var addonName = overlayLayer.Description;
|
||||
|
||||
DalamudInterface.Instance.AddonLifecycle.RegisterListener(AddonEvent.PreUpdate, addonName, OnOverlayAddonUpdate);
|
||||
DalamudInterface.Instance.AddonLifecycle.RegisterListener(AddonEvent.PreFinalize, addonName, OnOverlayAddonFinalize);
|
||||
}
|
||||
|
||||
BeginStateCheck();
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
DalamudInterface.Instance.AddonLifecycle.UnregisterListener(AddonEvent.PreFinalize, "NamePlate");
|
||||
DalamudInterface.Instance.AddonLifecycle.UnregisterListener(OnOverlayAddonFinalize, OnOverlayAddonUpdate);
|
||||
|
||||
foreach (var node in overlayNodes.SelectMany(nodeList => nodeList.Value)) {
|
||||
node.Dispose();
|
||||
}
|
||||
|
||||
overlayNodes.Clear();
|
||||
}
|
||||
|
||||
//
|
||||
// State management (framework thread)
|
||||
//
|
||||
|
||||
private void ClearState() {
|
||||
controllerState = ControllerState.WaitForNameplate;
|
||||
|
||||
foreach (var overlayLayer in Enum.GetValues<OverlayLayer>()) {
|
||||
addonState[overlayLayer] = OverlayAddonState.None;
|
||||
}
|
||||
}
|
||||
|
||||
private void BeginStateCheck() {
|
||||
DalamudInterface.Instance.Framework.Update -= CheckOverlayState;
|
||||
DalamudInterface.Instance.Framework.Update += CheckOverlayState;
|
||||
}
|
||||
|
||||
private void CheckOverlayState(IFramework framework) {
|
||||
switch (controllerState) {
|
||||
case ControllerState.WaitForNameplate:
|
||||
CheckNameplateReady();
|
||||
break;
|
||||
|
||||
case ControllerState.WaitForReady:
|
||||
CheckOverlayAddonsReady();
|
||||
break;
|
||||
|
||||
case ControllerState.Ready:
|
||||
DalamudInterface.Instance.Framework.Update -= CheckOverlayState;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckNameplateReady() {
|
||||
var nameplate = RaptureAtkUnitManager.Instance()->GetAddonByName("NamePlate");
|
||||
if (nameplate is null) return;
|
||||
if (!nameplate->IsReady) return;
|
||||
|
||||
foreach (var overlayLayer in Enum.GetValues<OverlayLayer>()) {
|
||||
var addon = RaptureAtkUnitManager.Instance()->GetAddonByName(overlayLayer.Description);
|
||||
|
||||
if (addon is null) {
|
||||
if (addonState[overlayLayer] == OverlayAddonState.None) {
|
||||
addonState[overlayLayer] = OverlayAddonState.WaitForReady;
|
||||
CreateOverlayAddon(overlayLayer).Open();
|
||||
}
|
||||
}
|
||||
else {
|
||||
addonState[overlayLayer] = OverlayAddonState.WaitForReady;
|
||||
}
|
||||
}
|
||||
|
||||
controllerState = ControllerState.WaitForReady;
|
||||
}
|
||||
|
||||
private void CheckOverlayAddonsReady() {
|
||||
var totalAddons = Enum.GetValues<OverlayLayer>().Length;
|
||||
var totalAddonsReady = 0;
|
||||
|
||||
foreach (var overlayLayer in Enum.GetValues<OverlayLayer>()) {
|
||||
var addon = RaptureAtkUnitManager.Instance()->GetAddonByName(overlayLayer.Description);
|
||||
if (addon is null) continue;
|
||||
if (!addon->IsReady) continue;
|
||||
|
||||
if (addonState[overlayLayer] is OverlayAddonState.WaitForReady) {
|
||||
AttachAllNodes(overlayLayer);
|
||||
addonState[overlayLayer] = OverlayAddonState.Ready;
|
||||
}
|
||||
totalAddonsReady++;
|
||||
}
|
||||
|
||||
if (totalAddonsReady == totalAddons) {
|
||||
controllerState = ControllerState.Ready;
|
||||
}
|
||||
}
|
||||
|
||||
private void AttachAllNodes(OverlayLayer layer) {
|
||||
if (!overlayNodes.TryGetValue(layer, out var list)) return;
|
||||
|
||||
var addon = RaptureAtkUnitManager.Instance()->GetAddonByName(layer.Description);
|
||||
if (addon is null) return;
|
||||
|
||||
foreach (var node in list) {
|
||||
AttachNode(addon, node);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Public node access
|
||||
//
|
||||
|
||||
public void CreateNode(Func<OverlayNode> creationFunction) => DalamudInterface.Instance.Framework.RunOnFrameworkThread(() => {
|
||||
AddNode(creationFunction());
|
||||
});
|
||||
|
||||
public void AddNode(OverlayNode node) => DalamudInterface.Instance.Framework.RunOnFrameworkThread(() => {
|
||||
overlayNodes.TryAdd(node.OverlayLayer, []);
|
||||
|
||||
if (overlayNodes[node.OverlayLayer].Contains(node)) return;
|
||||
|
||||
overlayNodes[node.OverlayLayer].Add(node);
|
||||
|
||||
if (addonState[node.OverlayLayer] is not OverlayAddonState.Ready) return;
|
||||
|
||||
var addon = RaptureAtkUnitManager.Instance()->GetAddonByName(node.OverlayLayer.Description);
|
||||
if (addon is null) return;
|
||||
|
||||
AttachNode(addon, node);
|
||||
});
|
||||
|
||||
public void RemoveNode(OverlayNode node) => DalamudInterface.Instance.Framework.RunOnFrameworkThread(() => {
|
||||
if (!overlayNodes.TryGetValue(node.OverlayLayer, out var list)) return;
|
||||
|
||||
if (list.Remove(node)) {
|
||||
node.Dispose();
|
||||
}
|
||||
});
|
||||
|
||||
public void RemoveAllNodes() => DalamudInterface.Instance.Framework.RunOnFrameworkThread(() => {
|
||||
foreach (var node in overlayNodes.SelectMany(set => set.Value).ToList()) {
|
||||
RemoveNode(node);
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Events
|
||||
//
|
||||
|
||||
private void OnNamePlatePreFinalize(AddonEvent type, AddonArgs args) {
|
||||
ClearState();
|
||||
|
||||
foreach (var overlayLayer in Enum.GetValues<OverlayLayer>()) {
|
||||
if (!overlayNodes.TryGetValue(overlayLayer, out var list)) continue;
|
||||
|
||||
foreach (var node in list) {
|
||||
node.DetachNode();
|
||||
}
|
||||
}
|
||||
|
||||
BeginStateCheck();
|
||||
}
|
||||
|
||||
private void OnOverlayAddonFinalize(AddonEvent type, AddonArgs args) {
|
||||
var addon = (AtkUnitBase*)args.Addon.Address;
|
||||
var overlayLayer = addon->DepthLayer.GetOverlayLayer();
|
||||
|
||||
if (overlayNodes.TryGetValue(overlayLayer, out var list)) {
|
||||
foreach (var node in list) {
|
||||
node.DetachNode();
|
||||
}
|
||||
}
|
||||
|
||||
addonState[overlayLayer] = OverlayAddonState.None;
|
||||
}
|
||||
|
||||
private void OnOverlayAddonUpdate(AddonEvent type, AddonArgs args) {
|
||||
var addon = (AtkUnitBase*)args.Addon.Address;
|
||||
var overlayLayer = addon->DepthLayer.GetOverlayLayer();
|
||||
|
||||
if (addonState[overlayLayer] is not OverlayAddonState.Ready) return;
|
||||
if (!overlayNodes.TryGetValue(overlayLayer, out var list)) return;
|
||||
|
||||
foreach (var node in list) {
|
||||
node.Update();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
private static OverlayAddon CreateOverlayAddon(OverlayLayer layer) => new() {
|
||||
Title = layer.Description,
|
||||
InternalName = layer.Description,
|
||||
DepthLayer = layer.DepthLayer,
|
||||
IsOverlayAddon = true,
|
||||
};
|
||||
|
||||
private static void AttachNode(AtkUnitBase* addon, OverlayNode node) {
|
||||
node.NodeId = (uint)addon->UldManager.NodeListCount + 1;
|
||||
node.AttachNode(addon);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user