171 lines
4.4 KiB
C#
171 lines
4.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using Dalamud.Interface.Textures;
|
|
using Dalamud.Interface.Textures.TextureWraps;
|
|
using Dalamud.Plugin.Services;
|
|
using Lumina.Excel.Sheets;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace LLib.ImGui;
|
|
|
|
public sealed class MapTextureService
|
|
{
|
|
private readonly ITextureProvider _textureProvider;
|
|
|
|
private readonly IDataManager _dataManager;
|
|
|
|
private readonly ILogger<MapTextureService> _logger;
|
|
|
|
private uint _loadedTerritoryType;
|
|
|
|
private ISharedImmediateTexture? _resolvedTexture;
|
|
|
|
private List<ISharedImmediateTexture>? _candidateTextures;
|
|
|
|
private bool _failed;
|
|
|
|
private long _retryAtMs;
|
|
|
|
private int _searchFrames;
|
|
|
|
private const int SearchTimeout = 120;
|
|
|
|
private const long RetryDelayMs = 30000L;
|
|
|
|
private ushort _sizeFactor;
|
|
|
|
private short _offsetX;
|
|
|
|
private short _offsetY;
|
|
|
|
public bool IsLoaded => _resolvedTexture != null;
|
|
|
|
public bool IsSearching => _candidateTextures != null;
|
|
|
|
public ushort SizeFactor => _sizeFactor;
|
|
|
|
public MapTextureService(ITextureProvider textureProvider, IDataManager dataManager, ILogger<MapTextureService> logger)
|
|
{
|
|
_textureProvider = textureProvider;
|
|
_dataManager = dataManager;
|
|
_logger = logger;
|
|
}
|
|
|
|
public bool LoadForTerritory(uint territoryType)
|
|
{
|
|
if (territoryType == _loadedTerritoryType && _resolvedTexture != null)
|
|
{
|
|
return true;
|
|
}
|
|
if (territoryType == _loadedTerritoryType && _failed)
|
|
{
|
|
return false;
|
|
}
|
|
if (territoryType == _loadedTerritoryType && _candidateTextures != null)
|
|
{
|
|
PollCandidates();
|
|
return _resolvedTexture != null;
|
|
}
|
|
if (territoryType == _loadedTerritoryType && Environment.TickCount64 < _retryAtMs)
|
|
{
|
|
return false;
|
|
}
|
|
_resolvedTexture = null;
|
|
_candidateTextures = null;
|
|
_failed = false;
|
|
_retryAtMs = 0L;
|
|
_loadedTerritoryType = territoryType;
|
|
_searchFrames = 0;
|
|
Map? map = _dataManager.GetExcelSheet<TerritoryType>().GetRowOrDefault(territoryType)?.Map.ValueNullable;
|
|
if (!map.HasValue || map.Value.RowId == 0)
|
|
{
|
|
_failed = true;
|
|
return false;
|
|
}
|
|
_sizeFactor = map.Value.SizeFactor;
|
|
_offsetX = map.Value.OffsetX;
|
|
_offsetY = map.Value.OffsetY;
|
|
string text = map.Value.Id.ExtractText();
|
|
if (string.IsNullOrEmpty(text))
|
|
{
|
|
_failed = true;
|
|
return false;
|
|
}
|
|
string[] array = text.Split('/');
|
|
if (array.Length != 2)
|
|
{
|
|
_failed = true;
|
|
return false;
|
|
}
|
|
string value = array[0];
|
|
string text2 = array[1];
|
|
List<string> list = new List<string> { $"ui/map/{text}/{value}{text2}_m.tex" };
|
|
if (text2 != "00")
|
|
{
|
|
list.Add($"ui/map/{value}/00/{value}00_m.tex");
|
|
}
|
|
_logger.LogDebug("Starting map texture search for territory {TerritoryType}: {Paths}", territoryType, string.Join(", ", list));
|
|
_candidateTextures = list.Select((string p) => _textureProvider.GetFromGame(p)).ToList();
|
|
PollCandidates();
|
|
return _resolvedTexture != null;
|
|
}
|
|
|
|
private void PollCandidates()
|
|
{
|
|
if (_candidateTextures == null)
|
|
{
|
|
return;
|
|
}
|
|
foreach (ISharedImmediateTexture candidateTexture in _candidateTextures)
|
|
{
|
|
if (candidateTexture.GetWrapOrDefault() != null)
|
|
{
|
|
_resolvedTexture = candidateTexture;
|
|
_candidateTextures = null;
|
|
return;
|
|
}
|
|
}
|
|
_searchFrames++;
|
|
if (_searchFrames >= 120)
|
|
{
|
|
_logger.LogWarning("Map texture search timed out for territory {TerritoryType}, retrying in {Delay}s", _loadedTerritoryType, 30L);
|
|
_candidateTextures = null;
|
|
_retryAtMs = Environment.TickCount64 + 30000;
|
|
}
|
|
}
|
|
|
|
public IDalamudTextureWrap? GetTextureWrap()
|
|
{
|
|
return _resolvedTexture?.GetWrapOrDefault();
|
|
}
|
|
|
|
public Vector2 WorldToUV(float worldX, float worldZ)
|
|
{
|
|
float num = (float)(int)_sizeFactor / 100f;
|
|
float x = (worldX * num + (float)_offsetX * num + 1024f) / 2048f;
|
|
float y = (worldZ * num + (float)_offsetY * num + 1024f) / 2048f;
|
|
return new Vector2(x, y);
|
|
}
|
|
|
|
public Vector2 UVToWorld(float u, float v)
|
|
{
|
|
float num = (float)(int)_sizeFactor / 100f;
|
|
float x = (u * 2048f - 1024f - (float)_offsetX * num) / num;
|
|
float y = (v * 2048f - 1024f - (float)_offsetY * num) / num;
|
|
return new Vector2(x, y);
|
|
}
|
|
|
|
public Vector2 WorldToScreen(float worldX, float worldZ, Vector2 imagePos, Vector2 imageSize)
|
|
{
|
|
Vector2 vector = WorldToUV(worldX, worldZ);
|
|
return imagePos + vector * imageSize;
|
|
}
|
|
|
|
public Vector2 ScreenToWorld(Vector2 screenPos, Vector2 imagePos, Vector2 imageSize)
|
|
{
|
|
Vector2 vector = (screenPos - imagePos) / imageSize;
|
|
return UVToWorld(vector.X, vector.Y);
|
|
}
|
|
}
|