1
0
Fork 0
forked from aly/qstbak
qstbak/Questionable/Questionable.Windows.SmartNavComponents/SubRegionMapPanel.cs
2026-08-17 20:29:32 +10:00

269 lines
7.5 KiB
C#

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<Vector2> Vertices = new List<Vector2>();
}
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<EditableSubRegion> _subRegions = new List<EditableSubRegion>();
public int SelectedIndex
{
get
{
return _editor.SelectedIndex;
}
set
{
_editor.SelectedIndex = value;
}
}
public bool IsDrawing => _editor.IsDrawing;
public IReadOnlyList<EditableSubRegion> 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<MapTextureService>());
_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<string, object> { [_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<SubRegionDefinition> subRegions = _zoneSubRegionService.GetSubRegions(territory);
if (subRegions == null)
{
return;
}
foreach (SubRegionDefinition item in subRegions)
{
List<Vector2> list = new List<Vector2>(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<SubRegionVertex> vertices, string id, string comment)
{
List<Vector2> list = new List<Vector2>(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<SubRegionVertex> ExportSelectedVertices()
{
if (_editor.SelectedIndex < 0 || _editor.SelectedIndex >= _subRegions.Count)
{
return new List<SubRegionVertex>();
}
EditableSubRegion editableSubRegion = _subRegions[_editor.SelectedIndex];
List<SubRegionVertex> list = new List<SubRegionVertex>(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<Vector2> 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<Vector2> vertices)
{
_subRegions.Add(new EditableSubRegion
{
Id = $"region_{_subRegions.Count + 1}",
Vertices = new List<Vector2>(vertices)
});
}
}