using System; using System.Collections.Generic; using System.Globalization; using System.Numerics; using System.Text.Json; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Services; using LLib.ImGui; using Microsoft.Extensions.Logging; using SmartNav.Data; using SmartNav.Model.Navigation; namespace Questionable.Windows.SmartNavComponents; internal sealed class SubRegionMapPanel : IEditablePolygonSet { internal sealed class EditableSubRegion { public string Id = string.Empty; public string Comment = string.Empty; public float MinYEdit; public float MaxYEdit; public bool HasMinY; public bool HasMaxY; public List Vertices = new List(); } private static readonly uint[] SubRegionColors = new uint[8] { 1610612991u, 1610678016u, 1627332640u, 1610678271u, 1627324671u, 1627389696u, 1627357184u, 1619001599u }; private readonly MapTextureService _mapService; private readonly MapPolygonEditor _editor; private readonly ZoneSubRegionService _zoneSubRegionService; private readonly IClientState _clientState; private readonly IObjectTable _objectTable; private uint _loadedTerritory; private readonly List _subRegions = new List(); public int SelectedIndex { get { return _editor.SelectedIndex; } set { _editor.SelectedIndex = value; } } public bool IsDrawing => _editor.IsDrawing; public IReadOnlyList SubRegions => _subRegions; int IEditablePolygonSet.PolygonCount => _subRegions.Count; public SubRegionMapPanel(ITextureProvider textureProvider, IDataManager dataManager, ZoneSubRegionService zoneSubRegionService, IClientState clientState, IObjectTable objectTable, ILoggerFactory loggerFactory) { _mapService = new MapTextureService(textureProvider, dataManager, loggerFactory.CreateLogger()); _editor = new MapPolygonEditor(_mapService); _zoneSubRegionService = zoneSubRegionService; _clientState = clientState; _objectTable = objectTable; } public void Draw(JsonSerializerOptions jsonOptions) { uint territoryType = _clientState.TerritoryType; EnsureLoaded(territoryType); _mapService.LoadForTerritory(territoryType); DrawToolbar(jsonOptions); Vector3? playerPosition = _objectTable.LocalPlayer?.Position; Vector2 contentRegionAvail = ImGui.GetContentRegionAvail(); _editor.Draw(this, playerPosition, new Vector2(contentRegionAvail.X, 500f)); } private void DrawToolbar(JsonSerializerOptions jsonOptions) { if (_editor.IsDrawing) { ImGui.TextColored(new Vector4(0.2f, 1f, 0.2f, 1f), "Drawing polygon... Click to add vertices. Close: click first vertex / Enter / double-click. Escape cancels."); } else { if (ImGui.Button("Draw Polygon")) { _editor.StartDrawing(); } ImGui.SameLine(); if (ImGui.Button("Reset View")) { _editor.ResetView(); } if (_editor.SelectedIndex >= 0 && _editor.SelectedIndex < _subRegions.Count) { ImGui.SameLine(); if (ImGui.Button("Delete Selected")) { _subRegions.RemoveAt(_editor.SelectedIndex); _editor.SelectedIndex = -1; } } ImGui.SameLine(); if (ImGui.Button("Copy All JSON")) { TerritorySubRegions value = BuildTerritorySubRegions(); ImGui.SetClipboardText(JsonSerializer.Serialize(new Dictionary { [_clientState.TerritoryType.ToString(CultureInfo.InvariantCulture)] = value }, jsonOptions)); } } if (_editor.SelectedIndex >= 0 && _editor.SelectedIndex < _subRegions.Count) { EditableSubRegion editableSubRegion = _subRegions[_editor.SelectedIndex]; ImGui.SetNextItemWidth(120f); ImGui.InputText("ID##mapSr", ref editableSubRegion.Id, 64); ImGui.SameLine(); ImGui.SetNextItemWidth(200f); ImGui.InputText("Comment##mapSr", ref editableSubRegion.Comment, 128); ImGui.Checkbox("MinY##mapSrCb", ref editableSubRegion.HasMinY); ImGui.SameLine(); using (ImRaii.Disabled(!editableSubRegion.HasMinY)) { ImGui.SetNextItemWidth(80f); ImGui.InputFloat("##minY", ref editableSubRegion.MinYEdit, 0f, 0f, "%.1f"); } ImGui.SameLine(); ImGui.Checkbox("MaxY##mapSrCb", ref editableSubRegion.HasMaxY); ImGui.SameLine(); using (ImRaii.Disabled(!editableSubRegion.HasMaxY)) { ImGui.SetNextItemWidth(80f); ImGui.InputFloat("##maxY", ref editableSubRegion.MaxYEdit, 0f, 0f, "%.1f"); } } } private void EnsureLoaded(uint territory) { if (territory == _loadedTerritory) { return; } _loadedTerritory = territory; _subRegions.Clear(); _editor.SelectedIndex = -1; if (!_zoneSubRegionService.HasSubRegions(territory)) { return; } IReadOnlyList subRegions = _zoneSubRegionService.GetSubRegions(territory); if (subRegions == null) { return; } foreach (SubRegionDefinition item in subRegions) { List list = new List(item.Vertices.Count); foreach (SubRegionVertex vertex in item.Vertices) { list.Add(new Vector2(vertex.X, vertex.Z)); } _subRegions.Add(new EditableSubRegion { Id = item.Id, Comment = (item.Comment ?? string.Empty), MinYEdit = item.MinY.GetValueOrDefault(), MaxYEdit = item.MaxY.GetValueOrDefault(), HasMinY = item.MinY.HasValue, HasMaxY = item.MaxY.HasValue, Vertices = list }); } } public TerritorySubRegions BuildTerritorySubRegions() { TerritorySubRegions territorySubRegions = new TerritorySubRegions(); foreach (EditableSubRegion subRegion in _subRegions) { SubRegionDefinition subRegionDefinition = new SubRegionDefinition { Id = subRegion.Id, Comment = (string.IsNullOrEmpty(subRegion.Comment) ? null : subRegion.Comment), MinY = (subRegion.HasMinY ? new float?(subRegion.MinYEdit) : ((float?)null)), MaxY = (subRegion.HasMaxY ? new float?(subRegion.MaxYEdit) : ((float?)null)) }; foreach (Vector2 vertex in subRegion.Vertices) { subRegionDefinition.Vertices.Add(new SubRegionVertex { X = MathF.Round(vertex.X, 1), Z = MathF.Round(vertex.Y, 1) }); } territorySubRegions.SubRegions.Add(subRegionDefinition); } return territorySubRegions; } public void ImportVertices(IReadOnlyList vertices, string id, string comment) { List list = new List(vertices.Count); foreach (SubRegionVertex vertex in vertices) { list.Add(new Vector2(vertex.X, vertex.Z)); } _subRegions.Add(new EditableSubRegion { Id = id, Comment = comment, Vertices = list }); _editor.SelectedIndex = _subRegions.Count - 1; } public List ExportSelectedVertices() { if (_editor.SelectedIndex < 0 || _editor.SelectedIndex >= _subRegions.Count) { return new List(); } EditableSubRegion editableSubRegion = _subRegions[_editor.SelectedIndex]; List list = new List(editableSubRegion.Vertices.Count); foreach (Vector2 vertex in editableSubRegion.Vertices) { list.Add(new SubRegionVertex { X = MathF.Round(vertex.X, 1), Z = MathF.Round(vertex.Y, 1) }); } return list; } IList IEditablePolygonSet.GetVertices(int polygonIndex) { return _subRegions[polygonIndex].Vertices; } uint IEditablePolygonSet.GetColor(int polygonIndex) { return SubRegionColors[polygonIndex % SubRegionColors.Length]; } string IEditablePolygonSet.GetLabel(int polygonIndex) { return _subRegions[polygonIndex].Id; } void IEditablePolygonSet.AddPolygon(IList vertices) { _subRegions.Add(new EditableSubRegion { Id = $"region_{_subRegions.Count + 1}", Vertices = new List(vertices) }); } }