forked from aly/qstbak
810 lines
26 KiB
C#
810 lines
26 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
|
using Dalamud.Interface;
|
|
using Dalamud.Interface.Textures.TextureWraps;
|
|
using Dalamud.Interface.Utility.Raii;
|
|
using Dalamud.Plugin.Services;
|
|
using Dalamud.Utility;
|
|
using LLib.ImGui;
|
|
using Lumina.Excel;
|
|
using Lumina.Excel.Sheets;
|
|
using Microsoft.Extensions.Logging;
|
|
using Questionable.Controller;
|
|
using Questionable.Data;
|
|
using Questionable.Functions;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Questing;
|
|
using Questionable.Windows.QuestComponents;
|
|
|
|
namespace Questionable.Windows.JournalComponents;
|
|
|
|
internal sealed class QuestMapComponent
|
|
{
|
|
private sealed record ZoneEntry(ushort TerritoryId, string Name, string DisplayName, string MapId);
|
|
|
|
private sealed class QuestMarker
|
|
{
|
|
public ElementId QuestId { get; }
|
|
|
|
public string QuestName { get; }
|
|
|
|
public Vector2 MapPixelPos { get; }
|
|
|
|
public Vector3 WorldPos { get; }
|
|
|
|
public Vector4 Color { get; set; }
|
|
|
|
public FontAwesomeIcon Icon { get; set; }
|
|
|
|
public string Status { get; set; }
|
|
|
|
public QuestMarker(ElementId questId, string questName, Vector2 mapPixelPos, Vector3 worldPos)
|
|
{
|
|
QuestId = questId;
|
|
QuestName = questName;
|
|
MapPixelPos = mapPixelPos;
|
|
WorldPos = worldPos;
|
|
Status = string.Empty;
|
|
}
|
|
}
|
|
|
|
private const float MapTextureSize = 2048f;
|
|
|
|
private const float MarkerRadius = 6f;
|
|
|
|
private const float MarkerHitRadius = 8f;
|
|
|
|
private const float MinZoom = 0.15f;
|
|
|
|
private const float MaxZoom = 3f;
|
|
|
|
private const float ZoomSpeed = 0.1f;
|
|
|
|
private readonly MapTextureService _mapService;
|
|
|
|
private readonly IDataManager _dataManager;
|
|
|
|
private readonly QuestRegistry _questRegistry;
|
|
|
|
private readonly QuestData _questData;
|
|
|
|
private readonly UiUtils _uiUtils;
|
|
|
|
private readonly TerritoryData _territoryData;
|
|
|
|
private readonly QuestFunctions _questFunctions;
|
|
|
|
private readonly QuestTooltipComponent _questTooltipComponent;
|
|
|
|
private readonly IGameGui _gameGui;
|
|
|
|
private readonly IClientState _clientState;
|
|
|
|
private Vector2 _offset = Vector2.Zero;
|
|
|
|
private float _zoom = 0.5f;
|
|
|
|
private bool _isDragging;
|
|
|
|
private Vector2 _lastMousePos;
|
|
|
|
private Vector2 _dragStartPos;
|
|
|
|
private int _selectedZoneIndex;
|
|
|
|
private int _selectedStatusFilter;
|
|
|
|
private bool _showUnobtainable;
|
|
|
|
private readonly string[] _statusFilterNames = new string[5] { "All", "Available", "Active", "Complete", "Locked" };
|
|
|
|
private List<ZoneEntry> _zones = new List<ZoneEntry>();
|
|
|
|
private string[] _zoneItems = Array.Empty<string>();
|
|
|
|
private List<QuestMarker> _currentMarkers = new List<QuestMarker>();
|
|
|
|
private Dictionary<ushort, List<(ElementId QuestId, string Name, Vector3 WorldPos, bool FromRegistry)>> _questsByTerritory = new Dictionary<ushort, List<(ElementId, string, Vector3, bool)>>();
|
|
|
|
private bool _needsRebuild = true;
|
|
|
|
private long _lastStyleRefresh;
|
|
|
|
private const long StyleRefreshIntervalMs = 3000L;
|
|
|
|
private QuestMarker? _selectedMarker;
|
|
|
|
private bool _centerOnMarker;
|
|
|
|
private string _zoneSearchText = string.Empty;
|
|
|
|
public Action<string>? SelectTabAction { get; set; }
|
|
|
|
public QuestChainComponent? QuestChainComponent { get; set; }
|
|
|
|
public QuestJournalUtils? QuestJournalUtils { get; set; }
|
|
|
|
public QuestMapComponent(ITextureProvider textureProvider, IDataManager dataManager, QuestRegistry questRegistry, QuestData questData, UiUtils uiUtils, TerritoryData territoryData, QuestFunctions questFunctions, QuestTooltipComponent questTooltipComponent, IGameGui gameGui, IClientState clientState, ILoggerFactory loggerFactory)
|
|
{
|
|
_mapService = new MapTextureService(textureProvider, dataManager, loggerFactory.CreateLogger<MapTextureService>());
|
|
_dataManager = dataManager;
|
|
_questRegistry = questRegistry;
|
|
_questData = questData;
|
|
_uiUtils = uiUtils;
|
|
_territoryData = territoryData;
|
|
_questFunctions = questFunctions;
|
|
_questTooltipComponent = questTooltipComponent;
|
|
_gameGui = gameGui;
|
|
_clientState = clientState;
|
|
}
|
|
|
|
public void DrawQuestMap()
|
|
{
|
|
if (_needsRebuild)
|
|
{
|
|
RebuildZoneList();
|
|
_needsRebuild = false;
|
|
}
|
|
DrawFilters();
|
|
ImGui.Separator();
|
|
if (_zones.Count == 0)
|
|
{
|
|
UiThemeUtils.EmptyState(FontAwesomeIcon.Map, "No quest zones available.");
|
|
return;
|
|
}
|
|
ZoneEntry zone = _zones[_selectedZoneIndex];
|
|
DrawMapCanvas(zone);
|
|
}
|
|
|
|
private void DrawFilters()
|
|
{
|
|
ImGuiStylePtr style = ImGui.GetStyle();
|
|
float num = 100f * ImGui.GetIO().FontGlobalScale;
|
|
float num2 = ImGui.CalcTextSize("My Zone").X + style.FramePadding.X * 2f;
|
|
float num3 = ImGui.GetFrameHeight() + style.ItemInnerSpacing.X + ImGui.CalcTextSize("Unobtainable").X;
|
|
float num4 = num + num2 + num3 + style.ItemSpacing.X * 4f;
|
|
string previewOverride = ((_zones.Count > 0 && _selectedZoneIndex < _zones.Count) ? _zones[_selectedZoneIndex].DisplayName : "No zones");
|
|
if (UiThemeUtils.SearchableCombo("##Zone", ref _selectedZoneIndex, _zoneItems, ref _zoneSearchText, 0f - num4, previewOverride))
|
|
{
|
|
OnZoneChanged();
|
|
}
|
|
ImGui.SameLine();
|
|
ImGui.SetNextItemWidth(100f * ImGui.GetIO().FontGlobalScale);
|
|
if (UiThemeUtils.ThemedCombo("##Status", ref _selectedStatusFilter, _statusFilterNames, _statusFilterNames.Length))
|
|
{
|
|
RebuildMarkers();
|
|
}
|
|
ImGui.SameLine();
|
|
if (UiThemeUtils.PillButton("My Zone", selected: false))
|
|
{
|
|
uint currentTerritory = _clientState.TerritoryType;
|
|
int num5 = _zones.FindIndex((ZoneEntry z) => z.TerritoryId == currentTerritory);
|
|
if (num5 >= 0)
|
|
{
|
|
_selectedZoneIndex = num5;
|
|
OnZoneChanged();
|
|
}
|
|
}
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.SetTooltip("Jump to your current zone");
|
|
}
|
|
ImGui.SameLine();
|
|
if (UiThemeUtils.WrappedCheckbox("Unobtainable", ref _showUnobtainable))
|
|
{
|
|
RebuildMarkers();
|
|
}
|
|
}
|
|
|
|
private static void DrawMapLegend(ImDrawListPtr drawList, Vector2 canvasPos, float canvasWidth)
|
|
{
|
|
(Vector4, string)[] array = new(Vector4, string)[4]
|
|
{
|
|
(UiThemeUtils.StatusAvailable, "Available"),
|
|
(UiThemeUtils.StatusComplete, "Complete"),
|
|
(UiThemeUtils.StatusLocked, "Locked"),
|
|
(UiThemeUtils.StatusUnobtainable, "Unobtainable")
|
|
};
|
|
float num = 0f;
|
|
(Vector4, string)[] array2 = array;
|
|
for (int i = 0; i < array2.Length; i++)
|
|
{
|
|
string item = array2[i].Item2;
|
|
num += 14f + ImGui.CalcTextSize(item).X + 14f;
|
|
}
|
|
num -= 14f;
|
|
float num2 = canvasPos.X + canvasWidth - num - 8f;
|
|
float num3 = canvasPos.Y + 8f;
|
|
float textLineHeight = ImGui.GetTextLineHeight();
|
|
Vector2 pMin = new Vector2(num2 - 8f, num3 - 2f);
|
|
Vector2 pMax = new Vector2(canvasPos.X + canvasWidth, num3 + textLineHeight + 2f);
|
|
Vector4 playButtonText = UiThemeUtils.PlayButtonText;
|
|
playButtonText.W = 0.75f;
|
|
drawList.AddRectFilled(pMin, pMax, ImGui.ColorConvertFloat4ToU32(playButtonText), 3f);
|
|
float num4 = num2;
|
|
array2 = array;
|
|
for (int i = 0; i < array2.Length; i++)
|
|
{
|
|
(Vector4, string) tuple = array2[i];
|
|
Vector4 item2 = tuple.Item1;
|
|
string item3 = tuple.Item2;
|
|
float y = num3 + textLineHeight * 0.5f;
|
|
drawList.AddCircleFilled(new Vector2(num4 + 4f, y), 4f, ImGui.ColorConvertFloat4ToU32(item2), 12);
|
|
num4 += 14f;
|
|
drawList.AddText(new Vector2(num4, num3), ImGui.ColorConvertFloat4ToU32(UiThemeUtils.DimTextColor), item3);
|
|
num4 += ImGui.CalcTextSize(item3).X + 14f;
|
|
}
|
|
}
|
|
|
|
private void DrawMapCanvas(ZoneEntry zone)
|
|
{
|
|
long tickCount = Environment.TickCount64;
|
|
if (tickCount - _lastStyleRefresh > 3000)
|
|
{
|
|
_lastStyleRefresh = tickCount;
|
|
RefreshMarkerStyles();
|
|
}
|
|
Vector2 contentRegionAvail = ImGui.GetContentRegionAvail();
|
|
Vector2 vector = new Vector2(contentRegionAvail.X, MathF.Max(contentRegionAvail.Y, 100f));
|
|
Vector2 cursorScreenPos = ImGui.GetCursorScreenPos();
|
|
ImGui.InvisibleButton("##mapCanvas", vector);
|
|
bool flag = ImGui.IsItemHovered();
|
|
ImDrawListPtr windowDrawList = ImGui.GetWindowDrawList();
|
|
Vector2 vector2 = cursorScreenPos + vector;
|
|
windowDrawList.AddRectFilled(cursorScreenPos, vector2, ImGui.ColorConvertFloat4ToU32(UiThemeUtils.PlayButtonText));
|
|
windowDrawList.PushClipRect(cursorScreenPos, vector2, intersectWithCurrentClipRect: true);
|
|
HandlePanZoom(cursorScreenPos, vector, flag);
|
|
if (_centerOnMarker && _selectedMarker != null)
|
|
{
|
|
_centerOnMarker = false;
|
|
_zoom = 1f;
|
|
Vector2 vector3 = (_selectedMarker.MapPixelPos - new Vector2(1024f)) * _zoom;
|
|
_offset = -vector3;
|
|
}
|
|
_mapService.LoadForTerritory(zone.TerritoryId);
|
|
IDalamudTextureWrap textureWrap = _mapService.GetTextureWrap();
|
|
if (textureWrap != null)
|
|
{
|
|
Vector2 vector4 = new Vector2(2048f * _zoom, 2048f * _zoom);
|
|
Vector2 vector5 = cursorScreenPos + _offset + vector * 0.5f - vector4 * 0.5f;
|
|
windowDrawList.AddImage(textureWrap.Handle, vector5, vector5 + vector4);
|
|
}
|
|
else
|
|
{
|
|
string text = (_mapService.IsSearching ? "Loading map..." : "Map not available for this zone.");
|
|
Vector2 pos = cursorScreenPos + vector * 0.5f - ImGui.CalcTextSize(text) * 0.5f;
|
|
windowDrawList.AddText(pos, ImGui.ColorConvertFloat4ToU32(UiThemeUtils.DimTextColor), text);
|
|
}
|
|
Vector2 vector6 = ScreenToMapPixel(cursorScreenPos, cursorScreenPos, vector);
|
|
Vector2 vector7 = ScreenToMapPixel(vector2, cursorScreenPos, vector);
|
|
float num = 12f;
|
|
QuestMarker questMarker = null;
|
|
foreach (QuestMarker currentMarker in _currentMarkers)
|
|
{
|
|
if (!(currentMarker.MapPixelPos.X < vector6.X - num) && !(currentMarker.MapPixelPos.X > vector7.X + num) && !(currentMarker.MapPixelPos.Y < vector6.Y - num) && !(currentMarker.MapPixelPos.Y > vector7.Y + num))
|
|
{
|
|
Vector2 vector8 = MapPixelToScreen(currentMarker.MapPixelPos, cursorScreenPos, vector);
|
|
uint col = ImGui.ColorConvertFloat4ToU32(currentMarker.Color);
|
|
uint col2 = ImGui.ColorConvertFloat4ToU32(new Vector4(0f, 0f, 0f, 0.8f));
|
|
float num2 = 6f * Math.Max(_zoom, 0.5f);
|
|
windowDrawList.AddCircleFilled(vector8, num2 + 1f, col2, 16);
|
|
windowDrawList.AddCircleFilled(vector8, num2, col, 16);
|
|
if (flag && !_isDragging && Vector2.Distance(ImGui.GetMousePos(), vector8) <= 8f * Math.Max(_zoom, 0.5f))
|
|
{
|
|
questMarker = currentMarker;
|
|
}
|
|
}
|
|
}
|
|
DrawMapLegend(windowDrawList, cursorScreenPos, vector.X);
|
|
if (_selectedMarker != null)
|
|
{
|
|
Vector2 center = MapPixelToScreen(_selectedMarker.MapPixelPos, cursorScreenPos, vector);
|
|
if (center.X >= cursorScreenPos.X && center.X <= vector2.X && center.Y >= cursorScreenPos.Y && center.Y <= vector2.Y)
|
|
{
|
|
float num3 = 6f * Math.Max(_zoom, 0.5f);
|
|
windowDrawList.AddCircle(center, num3 + 3f, ImGui.ColorConvertFloat4ToU32(new Vector4(1f, 1f, 1f, 0.9f)), 16, 2f);
|
|
}
|
|
}
|
|
bool flag2 = false;
|
|
bool flag3 = false;
|
|
Vector2 mousePos;
|
|
bool flag4;
|
|
uint col3;
|
|
float num5;
|
|
string text5;
|
|
float num7;
|
|
Vector2 pMin3;
|
|
Vector2 pMax3;
|
|
int num9;
|
|
Vector4 input;
|
|
float num6;
|
|
if (_selectedMarker != null)
|
|
{
|
|
Vector4 color = _selectedMarker.Color;
|
|
FontAwesomeIcon icon = _selectedMarker.Icon;
|
|
string status = _selectedMarker.Status;
|
|
string text2 = UiThemeUtils.CleanSeString(_selectedMarker.QuestName);
|
|
mousePos = ImGui.GetMousePos();
|
|
flag4 = ImGui.IsMouseReleased(ImGuiMouseButton.Left);
|
|
Vector2 vector9 = new Vector2(cursorScreenPos.X, vector2.Y - 34f);
|
|
Vector2 vector10 = vector2;
|
|
Vector2 pMin = vector9;
|
|
Vector2 pMax = vector10;
|
|
Vector4 playButtonText = UiThemeUtils.PlayButtonText;
|
|
playButtonText.W = 0.9f;
|
|
windowDrawList.AddRectFilled(pMin, pMax, ImGui.ColorConvertFloat4ToU32(playButtonText));
|
|
Vector2 pMin2 = vector9;
|
|
Vector2 pMax2 = new Vector2(vector10.X, vector9.Y + 1f);
|
|
playButtonText = UiThemeUtils.AccentColor;
|
|
playButtonText.W = 0.4f;
|
|
windowDrawList.AddRectFilled(pMin2, pMax2, ImGui.ColorConvertFloat4ToU32(playButtonText));
|
|
float y = vector9.Y + (34f - ImGui.GetTextLineHeight()) * 0.5f;
|
|
float num4 = vector9.X + 10f;
|
|
string text3 = icon.ToIconString();
|
|
Vector2 vector11;
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
vector11 = ImGui.CalcTextSize(text3);
|
|
}
|
|
windowDrawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize(), new Vector2(num4, y), ImGui.ColorConvertFloat4ToU32(color), text3);
|
|
num4 += vector11.X + 6f;
|
|
col3 = ImGui.ColorConvertFloat4ToU32(UiThemeUtils.PrimaryTextColor);
|
|
windowDrawList.AddText(new Vector2(num4, y), col3, text2);
|
|
num4 += ImGui.CalcTextSize(text2).X + 10f;
|
|
Vector2 pos2 = new Vector2(num4, y);
|
|
uint col4 = ImGui.ColorConvertFloat4ToU32(color);
|
|
ImU8String text4 = new ImU8String(2, 1);
|
|
text4.AppendLiteral("[");
|
|
text4.AppendFormatted(status);
|
|
text4.AppendLiteral("]");
|
|
windowDrawList.AddText(pos2, col4, text4);
|
|
num5 = vector9.Y + (34f - ImGui.GetTextLineHeight() - 8f) * 0.5f;
|
|
num6 = vector10.X - 10f;
|
|
text5 = "Open on Map";
|
|
Vector2 vector12 = ImGui.CalcTextSize(text5);
|
|
num7 = vector12.X + 16f;
|
|
float num8 = vector12.Y + 8f;
|
|
pMin3 = new Vector2(num6 - num7, num5);
|
|
pMax3 = new Vector2(num6, num5 + num8);
|
|
if (mousePos.X >= pMin3.X && mousePos.X <= pMax3.X && mousePos.Y >= pMin3.Y)
|
|
{
|
|
num9 = ((mousePos.Y <= pMax3.Y) ? 1 : 0);
|
|
if (num9 != 0)
|
|
{
|
|
playButtonText = UiThemeUtils.AccentColor;
|
|
playButtonText.W = 0.25f;
|
|
input = playButtonText;
|
|
goto IL_06fa;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
num9 = 0;
|
|
}
|
|
input = UiThemeUtils.AccentDimColor;
|
|
goto IL_06fa;
|
|
}
|
|
goto IL_085c;
|
|
IL_06fa:
|
|
windowDrawList.AddRectFilled(col: ImGui.ColorConvertFloat4ToU32(input), pMin: pMin3, pMax: pMax3, rounding: 3f);
|
|
windowDrawList.AddText(new Vector2(pMin3.X + 8f, pMin3.Y + 4f), col3, text5);
|
|
if (((uint)num9 & (flag4 ? 1u : 0u)) != 0)
|
|
{
|
|
flag3 = true;
|
|
}
|
|
num6 -= num7 + 6f;
|
|
string text6 = "Quest Chain";
|
|
Vector2 vector13 = ImGui.CalcTextSize(text6);
|
|
float num10 = vector13.X + 16f;
|
|
float num11 = vector13.Y + 8f;
|
|
Vector2 pMin4 = new Vector2(num6 - num10, num5);
|
|
Vector2 pMax4 = new Vector2(num6, num5 + num11);
|
|
int num12;
|
|
Vector4 input2;
|
|
if (mousePos.X >= pMin4.X && mousePos.X <= pMax4.X && mousePos.Y >= pMin4.Y)
|
|
{
|
|
num12 = ((mousePos.Y <= pMax4.Y) ? 1 : 0);
|
|
if (num12 != 0)
|
|
{
|
|
Vector4 playButtonText = UiThemeUtils.AccentColor;
|
|
playButtonText.W = 0.25f;
|
|
input2 = playButtonText;
|
|
goto IL_080c;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
num12 = 0;
|
|
}
|
|
input2 = UiThemeUtils.AccentDimColor;
|
|
goto IL_080c;
|
|
IL_080c:
|
|
windowDrawList.AddRectFilled(col: ImGui.ColorConvertFloat4ToU32(input2), pMin: pMin4, pMax: pMax4, rounding: 3f);
|
|
windowDrawList.AddText(new Vector2(pMin4.X + 8f, pMin4.Y + 4f), col3, text6);
|
|
if (((uint)num12 & (flag4 ? 1u : 0u)) != 0)
|
|
{
|
|
flag2 = true;
|
|
}
|
|
goto IL_085c;
|
|
IL_085c:
|
|
windowDrawList.PopClipRect();
|
|
if (flag3 && _selectedMarker != null && _selectedMarker.WorldPos != Vector3.Zero)
|
|
{
|
|
try
|
|
{
|
|
ZoneEntry zoneEntry = _zones[_selectedZoneIndex];
|
|
TerritoryType? rowOrDefault = _dataManager.GetExcelSheet<TerritoryType>().GetRowOrDefault(zoneEntry.TerritoryId);
|
|
if (rowOrDefault.HasValue)
|
|
{
|
|
Map? valueNullable = rowOrDefault.Value.Map.ValueNullable;
|
|
if (valueNullable.HasValue)
|
|
{
|
|
Vector2 vector14 = MapUtil.WorldToMap(new Vector2(_selectedMarker.WorldPos.X, _selectedMarker.WorldPos.Z), valueNullable.Value);
|
|
MapLinkPayload mapLink = new MapLinkPayload(zoneEntry.TerritoryId, valueNullable.Value.RowId, vector14.X, vector14.Y);
|
|
_gameGui.OpenMapWithMapLink(mapLink);
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
if (flag2 && _selectedMarker != null)
|
|
{
|
|
QuestChainComponent?.SelectQuest(_selectedMarker.QuestId);
|
|
SelectTabAction?.Invoke("Quest Chain");
|
|
}
|
|
if (questMarker != null && _questData.TryGetQuestInfo(questMarker.QuestId, out IQuestInfo questInfo))
|
|
{
|
|
_questTooltipComponent.Draw(questInfo);
|
|
}
|
|
bool flag5 = _isDragging && Vector2.Distance(ImGui.GetMousePos(), _dragStartPos) > 4f;
|
|
if (ImGui.IsMouseReleased(ImGuiMouseButton.Left) && !flag5)
|
|
{
|
|
if (questMarker != null)
|
|
{
|
|
_selectedMarker = questMarker;
|
|
}
|
|
else if (flag)
|
|
{
|
|
_selectedMarker = null;
|
|
}
|
|
}
|
|
if (questMarker != null && ImGui.IsMouseReleased(ImGuiMouseButton.Right) && !flag5)
|
|
{
|
|
_selectedMarker = questMarker;
|
|
ImU8String strId = new ImU8String(12, 1);
|
|
strId.AppendLiteral("##QuestPopup");
|
|
strId.AppendFormatted(questMarker.QuestId);
|
|
ImGui.OpenPopup(strId);
|
|
}
|
|
if (_selectedMarker != null && _questData.TryGetQuestInfo(_selectedMarker.QuestId, out IQuestInfo questInfo2))
|
|
{
|
|
Questionable.Model.Quest quest = null;
|
|
_questRegistry.TryGetQuest(_selectedMarker.QuestId, out quest);
|
|
QuestJournalUtils?.ShowContextMenu(questInfo2, quest, "QuestMapComponent");
|
|
}
|
|
}
|
|
|
|
private void HandlePanZoom(Vector2 canvasPos, Vector2 canvasSize, bool isHovered)
|
|
{
|
|
ImGuiIOPtr iO = ImGui.GetIO();
|
|
if (isHovered && MathF.Abs(iO.MouseWheel) > 0.01f)
|
|
{
|
|
float zoom = _zoom;
|
|
_zoom = Math.Clamp(_zoom + iO.MouseWheel * 0.1f, 0.15f, 3f);
|
|
Vector2 vector = ImGui.GetMousePos() - canvasPos - canvasSize * 0.5f - _offset;
|
|
_offset -= vector * (_zoom / zoom - 1f);
|
|
}
|
|
bool flag = ImGui.IsMouseDown(ImGuiMouseButton.Left) || ImGui.IsMouseDown(ImGuiMouseButton.Right) || ImGui.IsMouseDown(ImGuiMouseButton.Middle);
|
|
if (isHovered && flag)
|
|
{
|
|
if (!_isDragging)
|
|
{
|
|
_isDragging = true;
|
|
_lastMousePos = ImGui.GetMousePos();
|
|
_dragStartPos = _lastMousePos;
|
|
}
|
|
else
|
|
{
|
|
Vector2 mousePos = ImGui.GetMousePos();
|
|
Vector2 vector2 = mousePos - _lastMousePos;
|
|
_offset += vector2;
|
|
_lastMousePos = mousePos;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_isDragging = false;
|
|
}
|
|
}
|
|
|
|
private Vector2 MapPixelToScreen(Vector2 mapPixel, Vector2 canvasPos, Vector2 canvasSize)
|
|
{
|
|
Vector2 vector = (mapPixel - new Vector2(1024f)) * _zoom;
|
|
return canvasPos + canvasSize * 0.5f + _offset + vector;
|
|
}
|
|
|
|
private Vector2 ScreenToMapPixel(Vector2 screenPos, Vector2 canvasPos, Vector2 canvasSize)
|
|
{
|
|
return (screenPos - canvasPos - canvasSize * 0.5f - _offset) / _zoom + new Vector2(1024f);
|
|
}
|
|
|
|
private static float WorldToMapPixel(float worldCoord, float offset, float sizeFactor)
|
|
{
|
|
return 2048f * ((worldCoord + offset) * sizeFactor / 100f + 1024f) / 2048f;
|
|
}
|
|
|
|
private void RebuildZoneList()
|
|
{
|
|
HashSet<ushort> hashSet = new HashSet<ushort>();
|
|
Dictionary<ushort, int> territoryQuests = new Dictionary<ushort, int>();
|
|
_questsByTerritory.Clear();
|
|
foreach (Questionable.Model.Quest allQuest in _questRegistry.AllQuests)
|
|
{
|
|
if (allQuest.Root.Disabled)
|
|
{
|
|
continue;
|
|
}
|
|
QuestSequence questSequence = allQuest.FindSequence(0);
|
|
if (questSequence == null || questSequence.Steps.Count == 0)
|
|
{
|
|
continue;
|
|
}
|
|
QuestStep questStep = questSequence.Steps[0];
|
|
if (questStep.TerritoryId != 0 && questStep.Position.HasValue)
|
|
{
|
|
hashSet.Add(questStep.TerritoryId);
|
|
territoryQuests[questStep.TerritoryId] = territoryQuests.GetValueOrDefault(questStep.TerritoryId) + 1;
|
|
if (!_questsByTerritory.TryGetValue(questStep.TerritoryId, out List<(ElementId, string, Vector3, bool)> value))
|
|
{
|
|
value = new List<(ElementId, string, Vector3, bool)>();
|
|
_questsByTerritory[questStep.TerritoryId] = value;
|
|
}
|
|
value.Add((allQuest.Id, allQuest.Info.Name, questStep.Position.Value, true));
|
|
}
|
|
}
|
|
HashSet<ElementId> hashSet2 = new HashSet<ElementId>(_questRegistry.AllQuests.Select((Questionable.Model.Quest q) => q.Id));
|
|
try
|
|
{
|
|
ExcelSheet<Lumina.Excel.Sheets.Quest> excelSheet = _dataManager.GetExcelSheet<Lumina.Excel.Sheets.Quest>();
|
|
foreach (var (elementId2, questInfo2) in _questData.AllQuests)
|
|
{
|
|
if (!(elementId2 is QuestId questId) || hashSet2.Contains(elementId2))
|
|
{
|
|
continue;
|
|
}
|
|
try
|
|
{
|
|
Lumina.Excel.Sheets.Quest? rowOrDefault = excelSheet.GetRowOrDefault((uint)(questId.Value + 65536));
|
|
if (!rowOrDefault.HasValue)
|
|
{
|
|
continue;
|
|
}
|
|
Level? valueNullable = rowOrDefault.Value.IssuerLocation.ValueNullable;
|
|
if (!valueNullable.HasValue)
|
|
{
|
|
continue;
|
|
}
|
|
ushort num = (ushort)valueNullable.Value.Territory.RowId;
|
|
if (num != 0)
|
|
{
|
|
hashSet.Add(num);
|
|
territoryQuests[num] = territoryQuests.GetValueOrDefault(num) + 1;
|
|
if (!_questsByTerritory.TryGetValue(num, out List<(ElementId, string, Vector3, bool)> value2))
|
|
{
|
|
value2 = new List<(ElementId, string, Vector3, bool)>();
|
|
_questsByTerritory[num] = value2;
|
|
}
|
|
Vector3 item = new Vector3(valueNullable.Value.X, valueNullable.Value.Y, valueNullable.Value.Z);
|
|
value2.Add((questId, questInfo2.Name, item, false));
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
_zones = (from z in hashSet.Select(delegate(ushort id)
|
|
{
|
|
string name = _territoryData.GetName(id);
|
|
string mapIdForTerritory = GetMapIdForTerritory(id);
|
|
int num3 = territoryQuests[id];
|
|
string text = $"({num3} quest{((num3 != 1) ? "s" : "")})";
|
|
string displayName = ((name != null) ? (name + " " + text) : $"Zone {id} {text}");
|
|
return new ZoneEntry(id, name ?? $"Zone {id}", displayName, mapIdForTerritory);
|
|
})
|
|
orderby z.Name
|
|
select z).ToList();
|
|
_zoneItems = _zones.Select((ZoneEntry z) => z.DisplayName).ToArray();
|
|
if (_zones.Count > 0)
|
|
{
|
|
uint currentTerritory = _clientState.TerritoryType;
|
|
int num2 = _zones.FindIndex((ZoneEntry z) => z.TerritoryId == currentTerritory);
|
|
_selectedZoneIndex = ((num2 >= 0) ? num2 : Math.Clamp(_selectedZoneIndex, 0, _zones.Count - 1));
|
|
RebuildMarkers();
|
|
}
|
|
}
|
|
|
|
private string GetMapIdForTerritory(ushort territoryId)
|
|
{
|
|
try
|
|
{
|
|
RowRef<Map> map = _dataManager.GetExcelSheet<TerritoryType>().GetRow(territoryId).Map;
|
|
if (map.RowId != 0)
|
|
{
|
|
return map.Value.Id.ExtractText();
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
return string.Empty;
|
|
}
|
|
|
|
private Map? LoadMapRow(ushort territoryId)
|
|
{
|
|
try
|
|
{
|
|
TerritoryType row = _dataManager.GetExcelSheet<TerritoryType>().GetRow(territoryId);
|
|
return (row.Map.RowId != 0) ? new Map?(row.Map.Value) : ((Map?)null);
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private void OnZoneChanged()
|
|
{
|
|
_offset = Vector2.Zero;
|
|
_zoom = 0.5f;
|
|
_selectedMarker = null;
|
|
RebuildMarkers();
|
|
}
|
|
|
|
private void RebuildMarkers()
|
|
{
|
|
_currentMarkers.Clear();
|
|
if (_zones.Count == 0 || _selectedZoneIndex >= _zones.Count)
|
|
{
|
|
return;
|
|
}
|
|
ZoneEntry zoneEntry = _zones[_selectedZoneIndex];
|
|
if (!_questsByTerritory.TryGetValue(zoneEntry.TerritoryId, out List<(ElementId, string, Vector3, bool)> value))
|
|
{
|
|
return;
|
|
}
|
|
Map? map = LoadMapRow(zoneEntry.TerritoryId);
|
|
foreach (var (elementId, questName, worldPos, _) in value)
|
|
{
|
|
if ((_showUnobtainable || !_questFunctions.IsQuestUnobtainable(elementId)) && PassesStatusFilter(elementId))
|
|
{
|
|
Vector2 mapPixelPos = (map.HasValue ? new Vector2(WorldToMapPixel(worldPos.X, map.Value.OffsetX, (int)map.Value.SizeFactor), WorldToMapPixel(worldPos.Z, map.Value.OffsetY, (int)map.Value.SizeFactor)) : new Vector2(1024f + worldPos.X, 1024f + worldPos.Z));
|
|
_currentMarkers.Add(new QuestMarker(elementId, questName, mapPixelPos, worldPos));
|
|
}
|
|
}
|
|
RefreshMarkerStyles();
|
|
}
|
|
|
|
private void RefreshMarkerStyles()
|
|
{
|
|
foreach (QuestMarker currentMarker in _currentMarkers)
|
|
{
|
|
(Vector4 Color, FontAwesomeIcon Icon, string Status) questStyle = _uiUtils.GetQuestStyle(currentMarker.QuestId);
|
|
Vector4 item = questStyle.Color;
|
|
FontAwesomeIcon item2 = questStyle.Icon;
|
|
string item3 = questStyle.Status;
|
|
currentMarker.Color = item;
|
|
currentMarker.Icon = item2;
|
|
currentMarker.Status = item3;
|
|
}
|
|
if (_selectedMarker != null && !_currentMarkers.Contains(_selectedMarker))
|
|
{
|
|
var (color, icon, status) = _uiUtils.GetQuestStyle(_selectedMarker.QuestId);
|
|
_selectedMarker.Color = color;
|
|
_selectedMarker.Icon = icon;
|
|
_selectedMarker.Status = status;
|
|
}
|
|
_lastStyleRefresh = Environment.TickCount64;
|
|
}
|
|
|
|
private bool PassesStatusFilter(ElementId questId)
|
|
{
|
|
return _selectedStatusFilter switch
|
|
{
|
|
1 => !_questFunctions.IsQuestLocked(questId) && _questFunctions.IsReadyToAcceptQuest(questId) && !_questFunctions.IsQuestAcceptedOrComplete(questId),
|
|
2 => _questFunctions.IsQuestAccepted(questId),
|
|
3 => _questFunctions.IsQuestComplete(questId),
|
|
4 => _questFunctions.IsQuestLocked(questId) || !_questFunctions.IsReadyToAcceptQuest(questId),
|
|
_ => true,
|
|
};
|
|
}
|
|
|
|
public void SelectZone(ushort territoryId)
|
|
{
|
|
if (_needsRebuild)
|
|
{
|
|
RebuildZoneList();
|
|
_needsRebuild = false;
|
|
}
|
|
for (int i = 0; i < _zones.Count; i++)
|
|
{
|
|
if (_zones[i].TerritoryId == territoryId)
|
|
{
|
|
_selectedZoneIndex = i;
|
|
OnZoneChanged();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SelectQuest(ElementId questId, ushort territoryId)
|
|
{
|
|
SelectZone(territoryId);
|
|
foreach (QuestMarker currentMarker in _currentMarkers)
|
|
{
|
|
if (currentMarker.QuestId == questId)
|
|
{
|
|
_selectedMarker = currentMarker;
|
|
_centerOnMarker = true;
|
|
return;
|
|
}
|
|
}
|
|
if (!_questData.TryGetQuestInfo(questId, out IQuestInfo questInfo))
|
|
{
|
|
return;
|
|
}
|
|
Vector2 mapPixelPos = new Vector2(1024f);
|
|
Vector3 worldPos = Vector3.Zero;
|
|
if (questId is QuestId questId2)
|
|
{
|
|
try
|
|
{
|
|
Lumina.Excel.Sheets.Quest? rowOrDefault = _dataManager.GetExcelSheet<Lumina.Excel.Sheets.Quest>().GetRowOrDefault((uint)(questId2.Value + 65536));
|
|
if (rowOrDefault.HasValue)
|
|
{
|
|
Level? valueNullable = rowOrDefault.Value.IssuerLocation.ValueNullable;
|
|
if (valueNullable.HasValue)
|
|
{
|
|
worldPos = new Vector3(valueNullable.Value.X, valueNullable.Value.Y, valueNullable.Value.Z);
|
|
Map? map = LoadMapRow(territoryId);
|
|
if (map.HasValue)
|
|
{
|
|
mapPixelPos = new Vector2(WorldToMapPixel(worldPos.X, map.Value.OffsetX, (int)map.Value.SizeFactor), WorldToMapPixel(worldPos.Z, map.Value.OffsetY, (int)map.Value.SizeFactor));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
QuestMarker questMarker = new QuestMarker(questId, questInfo.Name, mapPixelPos, worldPos);
|
|
(Vector4 Color, FontAwesomeIcon Icon, string Status) questStyle = _uiUtils.GetQuestStyle(questId);
|
|
Vector4 item = questStyle.Color;
|
|
FontAwesomeIcon item2 = questStyle.Icon;
|
|
string item3 = questStyle.Status;
|
|
questMarker.Color = item;
|
|
questMarker.Icon = item2;
|
|
questMarker.Status = item3;
|
|
_selectedMarker = questMarker;
|
|
_centerOnMarker = true;
|
|
}
|
|
|
|
public void MarkDirty()
|
|
{
|
|
_needsRebuild = true;
|
|
_selectedZoneIndex = -1;
|
|
_selectedMarker = null;
|
|
_offset = Vector2.Zero;
|
|
_zoom = 0.5f;
|
|
}
|
|
}
|