qstbak/Questionable/Questionable.Windows.JournalComponents/QuestMapComponent.cs
2026-08-17 20:29:32 +10:00

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;
}
}