using System.Numerics; using Dalamud.Bindings.ImGui; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.Gui; using Dalamud.Game.Player; using Dalamud.Utility; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// /// Widget to display the Object Table. /// internal class ObjectTableWidget : IDataWindowWidget { private bool resolveGameData; private bool drawCharacters; private float maxCharaDrawDistance = 20.0f; /// public string[]? CommandShortcuts { get; init; } = { "ot", "objecttable" }; /// public string DisplayName { get; init; } = "Object Table"; /// public bool Ready { get; set; } /// public void Load() { this.Ready = true; } /// public void Draw() { ImGui.Checkbox("Resolve GameData"u8, ref this.resolveGameData); var chatGui = Service.Get(); var clientState = Service.Get(); var playerState = Service.Get(); var gameGui = Service.Get(); var objectTable = Service.Get(); var stateString = string.Empty; if (objectTable.LocalPlayer == null) { ImGui.Text("LocalPlayer null."u8); } else if (clientState.IsPvPExcludingDen) { ImGui.Text("Cannot access object table while in PvP."u8); } else { stateString += $"ObjectTableLen: {objectTable.Length}\n"; stateString += $"LocalPlayerName: {playerState.CharacterName}\n"; stateString += $"CurrentWorldName: {(this.resolveGameData ? playerState.CurrentWorld.ValueNullable?.Name : playerState.CurrentWorld.RowId.ToString())}\n"; stateString += $"HomeWorldName: {(this.resolveGameData ? playerState.HomeWorld.ValueNullable?.Name : playerState.HomeWorld.RowId.ToString())}\n"; stateString += $"LocalCID: {playerState.ContentId:X}\n"; stateString += $"LastLinkedItem: {chatGui.LastLinkedItemId}\n"; stateString += $"TerritoryType: {clientState.TerritoryType}\n\n"; ImGui.Text(stateString); ImGui.Checkbox("Draw characters on screen"u8, ref this.drawCharacters); ImGui.SliderFloat("Draw Distance"u8, ref this.maxCharaDrawDistance, 2f, 40f); for (var i = 0; i < objectTable.Length; i++) { var obj = objectTable[i]; if (obj == null) continue; Util.PrintGameObject(obj, i.ToString(), this.resolveGameData); if (this.drawCharacters && gameGui.WorldToScreen(obj.Position, out var screenCoords)) { // So, while WorldToScreen will return false if the point is off of game client screen, to // to avoid performance issues, we have to manually determine if creating a window would // produce a new viewport, and skip rendering it if so var objectText = $"{obj.Address.ToInt64():X}:{obj.GameObjectId:X}[{i}] - {obj.ObjectKind} - {obj.Name}"; var screenPos = ImGui.GetMainViewport().Pos; var screenSize = ImGui.GetMainViewport().Size; var windowSize = ImGui.CalcTextSize(objectText); // Add some extra safety padding windowSize.X += ImGui.GetStyle().WindowPadding.X + 10; windowSize.Y += ImGui.GetStyle().WindowPadding.Y + 10; if (screenCoords.X + windowSize.X > screenPos.X + screenSize.X || screenCoords.Y + windowSize.Y > screenPos.Y + screenSize.Y) continue; if (obj.YalmDistanceX > this.maxCharaDrawDistance) continue; ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y)); ImGui.SetNextWindowBgAlpha(Math.Max(1f - (obj.YalmDistanceX / this.maxCharaDrawDistance), 0.2f)); if (ImGui.Begin( $"Actor{i}##ActorWindow{i}", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoMouseInputs | ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNav)) ImGui.Text(objectText); ImGui.End(); } } } } }