diff --git a/Quest Map/Commands.cs b/Quest Map/Commands.cs index 073a777..bbd9859 100644 --- a/Quest Map/Commands.cs +++ b/Quest Map/Commands.cs @@ -8,13 +8,13 @@ namespace QuestMap { internal Commands(Plugin plugin) { this.Plugin = plugin; - this.Plugin.Interface.CommandManager.AddHandler("/quests", new CommandInfo(this.OnCommand) { + this.Plugin.CommandManager.AddHandler("/quests", new CommandInfo(this.OnCommand) { HelpMessage = "Show Quest Map", }); } public void Dispose() { - this.Plugin.Interface.CommandManager.RemoveHandler("/quests"); + this.Plugin.CommandManager.RemoveHandler("/quests"); } private void OnCommand(string command, string args) { diff --git a/Quest Map/DalamudPlugin.cs b/Quest Map/DalamudPlugin.cs deleted file mode 100644 index 0dfaf40..0000000 --- a/Quest Map/DalamudPlugin.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Dalamud.Plugin; - -namespace QuestMap { - // ReSharper disable once UnusedType.Global - public class DalamudPlugin : IDalamudPlugin { - internal const string PluginName = "Quest Map"; - - public string Name => PluginName; - - private Plugin? Plugin { get; set; } - - public void Initialize(DalamudPluginInterface pluginInterface) { - this.Plugin = new Plugin(pluginInterface); - } - - public void Dispose() { - this.Plugin?.Dispose(); - } - } -} diff --git a/Quest Map/FodyWeavers.xml b/Quest Map/FodyWeavers.xml deleted file mode 100644 index e5727bf..0000000 --- a/Quest Map/FodyWeavers.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/Quest Map/Node.cs b/Quest Map/Node.cs index 62606b2..344a24a 100644 --- a/Quest Map/Node.cs +++ b/Quest Map/Node.cs @@ -152,15 +152,15 @@ namespace QuestMap { internal static IEnumerable PreviousQuests(this Quest quest) { if (quest.PreviousQuest0.Row != 0) { - yield return quest.PreviousQuest0.Value; + yield return quest.PreviousQuest0.Value!; } if (quest.PreviousQuest1.Row != 0) { - yield return quest.PreviousQuest1.Value; + yield return quest.PreviousQuest1.Value!; } if (quest.PreviousQuest2.Row != 0) { - yield return quest.PreviousQuest2.Value; + yield return quest.PreviousQuest2.Value!; } } } diff --git a/Quest Map/Plugin.cs b/Quest Map/Plugin.cs index f35b386..8dfbbef 100644 --- a/Quest Map/Plugin.cs +++ b/Quest Map/Plugin.cs @@ -1,21 +1,44 @@ -using System; -using System.Threading.Channels; +using System.Threading.Channels; +using Dalamud.Data; +using Dalamud.Game.ClientState; +using Dalamud.Game.Command; +using Dalamud.Game.Gui; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.IoC; using Dalamud.Plugin; -using Microsoft.Msagl.Core.Layout; using XivCommon; namespace QuestMap { - internal class Plugin : IDisposable { - internal DalamudPluginInterface Interface { get; } + // ReSharper disable once ClassNeverInstantiated.Global + internal class Plugin : IDalamudPlugin { + public string Name => "Quest Map"; + + [PluginService] + internal DalamudPluginInterface Interface { get; init; } = null!; + + [PluginService] + internal ClientState ClientState { get; init; } = null!; + + [PluginService] + internal CommandManager CommandManager { get; init; } = null!; + + [PluginService] + internal DataManager DataManager { get; init; } = null!; + + [PluginService] + internal GameGui GameGui { get; init; } = null!; + + [PluginService] + internal SeStringManager SeStringManager { get; init; } = null!; + internal XivCommonBase Common { get; } internal Configuration Config { get; } internal Quests Quests { get; } internal PluginUi Ui { get; } private Commands Commands { get; } - internal Plugin(DalamudPluginInterface pluginInterface) { - this.Interface = pluginInterface; - this.Common = new XivCommonBase(pluginInterface); + public Plugin() { + this.Common = new XivCommonBase(); this.Config = this.Interface.GetPluginConfig() as Configuration ?? new Configuration(); var graphChannel = Channel.CreateUnbounded(); diff --git a/Quest Map/PluginUi.cs b/Quest Map/PluginUi.cs index 3159c6d..a3da52a 100644 --- a/Quest Map/PluginUi.cs +++ b/Quest Map/PluginUi.cs @@ -60,20 +60,20 @@ namespace QuestMap { this.Refilter(); - this.Plugin.Interface.UiBuilder.OnBuildUi += this.Draw; - this.Plugin.Interface.UiBuilder.OnOpenConfigUi += this.OpenConfig; + this.Plugin.Interface.UiBuilder.Draw += this.Draw; + this.Plugin.Interface.UiBuilder.OpenConfigUi += this.OpenConfig; } public void Dispose() { - this.Plugin.Interface.UiBuilder.OnOpenConfigUi -= this.OpenConfig; - this.Plugin.Interface.UiBuilder.OnBuildUi -= this.Draw; + this.Plugin.Interface.UiBuilder.OpenConfigUi -= this.OpenConfig; + this.Plugin.Interface.UiBuilder.Draw -= this.Draw; foreach (var icon in this.Icons.Values) { icon.Dispose(); } } - private void OpenConfig(object sender, EventArgs e) { + private void OpenConfig() { this.Show = true; } @@ -81,7 +81,7 @@ namespace QuestMap { this.FilteredQuests.Clear(); var filterLower = this._filter.ToLowerInvariant(); - var filtered = this.Plugin.Interface.Data.GetExcelSheet() + var filtered = this.Plugin.DataManager.GetExcelSheet()! .Where(quest => { if (quest.Name.ToString().Length == 0) { return false; @@ -198,7 +198,7 @@ namespace QuestMap { ImGui.SetNextWindowSize(new Vector2(675, 600), ImGuiCond.FirstUseEver); - if (!ImGui.Begin(DalamudPlugin.PluginName, ref this.Show, ImGuiWindowFlags.MenuBar)) { + if (!ImGui.Begin(this.Plugin.Name, ref this.Show, ImGuiWindowFlags.MenuBar)) { ImGui.End(); return; } @@ -357,7 +357,7 @@ namespace QuestMap { var remove = 0u; foreach (var id in this.InfoWindows) { - var quest = this.Plugin.Interface.Data.GetExcelSheet().GetRow(id); + var quest = this.Plugin.DataManager.GetExcelSheet()!.GetRow(id); if (quest == null) { continue; } @@ -393,13 +393,15 @@ namespace QuestMap { ImGui.PopFont(); } - TextureWrap GetIcon(uint id) { + TextureWrap? GetIcon(uint id) { if (this.Icons.TryGetValue(id, out var wrap)) { return wrap; } - wrap = this.Plugin.Interface.Data.GetImGuiTextureIcon(this.Plugin.Interface.ClientState.ClientLanguage, (int) id); - this.Icons[id] = wrap; + wrap = this.Plugin.DataManager.GetImGuiTextureIcon(this.Plugin.ClientState.ClientLanguage, id); + if (wrap != null) { + this.Icons[id] = wrap; + } return wrap; } @@ -408,13 +410,19 @@ namespace QuestMap { if (quest.Icon != 0) { var header = GetIcon(quest.Icon); - textWrap = header.Width; - ImGui.Image(header.ImGuiHandle, new Vector2(header.Width, header.Height)); + if (header != null) { + textWrap = header.Width; + ImGui.Image(header.ImGuiHandle, new Vector2(header.Width, header.Height)); + } } var rewards = new List(); - var paramGrow = this.Plugin.Interface.Data.GetExcelSheet().GetRow(quest.ClassJobLevel0); - var xp = quest.ExpFactor * paramGrow.ScaledQuestXP * paramGrow.QuestExpModifier / 100; + var paramGrow = this.Plugin.DataManager.GetExcelSheet()!.GetRow(quest.ClassJobLevel0); + var xp = 0; + if (paramGrow != null) { + xp = quest.ExpFactor * paramGrow.ScaledQuestXP * paramGrow.QuestExpModifier / 100; + } + if (xp > 0) { rewards.Add($"Exp: {xp:N0}"); } @@ -437,17 +445,23 @@ namespace QuestMap { ImGui.TextUnformatted(label); - var maxHeight = items.Select(entry => GetIcon(entry.icon)).Max(image => image.Height); + var maxHeight = items + .Select(entry => GetIcon(entry.icon)) + .Where(image => image != null) + .Max(image => image!.Height); var originalY = ImGui.GetCursorPosY(); foreach (var (name, icon, qty) in items) { var image = GetIcon(icon); - if (image.Height < maxHeight) { - ImGui.SetCursorPosY(originalY + (maxHeight - image.Height) / 2f); + if (image != null) { + if (image.Height < maxHeight) { + ImGui.SetCursorPosY(originalY + (maxHeight - image.Height) / 2f); + } + + ImGui.Image(image.ImGuiHandle, new Vector2(image.Width, image.Height)); + Util.Tooltip(name.ToString()); } - ImGui.Image(image.ImGuiHandle, new Vector2(image.Width, image.Height)); - Util.Tooltip(name.ToString()); if (qty > 1) { var oldSpacing = ImGui.GetStyle().ItemSpacing; ImGui.GetStyle().ItemSpacing = new Vector2(2, 0); @@ -478,12 +492,12 @@ namespace QuestMap { var amount = quest.ItemCountCatalyst[i]; if (catalyst.Row != 0) { - additionalRewards.Add((this.Convert(catalyst.Value.Name), catalyst.Value.Icon, amount)); + additionalRewards.Add((this.Convert(catalyst.Value!.Name), catalyst.Value.Icon, amount)); } } foreach (var generalAction in quest.GeneralActionReward.Where(row => row.Row != 0)) { - additionalRewards.Add((this.Convert(generalAction.Value.Name), (uint) generalAction.Value.Icon, 1)); + additionalRewards.Add((this.Convert(generalAction.Value!.Name), (uint) generalAction.Value.Icon, 1)); } if (this.Plugin.Quests.ActionRewards.TryGetValue(quest.RowId, out var action)) { @@ -495,7 +509,7 @@ namespace QuestMap { } if (quest.OtherReward.Row != 0) { - additionalRewards.Add((this.Convert(quest.OtherReward.Value.Name), quest.OtherReward.Value.Icon, 1)); + additionalRewards.Add((this.Convert(quest.OtherReward.Value!.Name), quest.OtherReward.Value.Icon, 1)); } if (quest.ReputationReward > 0) { @@ -506,7 +520,7 @@ namespace QuestMap { } if (quest.TomestoneReward > 0) { - var tomestone = this.Plugin.Interface.Data.GetExcelSheet().First(row => row.Tomestones.Row == quest.TomestoneReward); + var tomestone = this.Plugin.DataManager.GetExcelSheet()!.FirstOrDefault(row => row.Tomestones.Row == quest.TomestoneReward); var item = tomestone?.Item?.Value; if (item != null) { additionalRewards.Add((this.Convert(item.Name), item.Icon, quest.TomestoneCountReward)); @@ -519,9 +533,9 @@ namespace QuestMap { quest.ItemReward0 .Zip(quest.ItemCountReward0, (id, qty) => (id, qty)) .Where(entry => entry.id != 0) - .Select(entry => (item: this.Plugin.Interface.Data.GetExcelSheet().GetRow(entry.id), entry.qty)) + .Select(entry => (item: this.Plugin.DataManager.GetExcelSheet()!.GetRow(entry.id), entry.qty)) .Where(entry => entry.item != null) - .Select(entry => (this.Convert(entry.item.Name), (uint) entry.item.Icon, entry.qty)) + .Select(entry => (this.Convert(entry.item!.Name), (uint) entry.item.Icon, entry.qty)) .Concat(additionalRewards) ); @@ -532,7 +546,7 @@ namespace QuestMap { .Where(entry => entry.row.Row != 0) .Select(entry => (item: entry.row.Value, entry.qty)) .Where(entry => entry.item != null) - .Select(entry => (this.Convert(entry.item.Name), (uint) entry.item.Icon, entry.qty)) + .Select(entry => (this.Convert(entry.item!.Name), (uint) entry.item.Icon, entry.qty)) ); } @@ -543,8 +557,10 @@ namespace QuestMap { var icon = instance.ContentType.Value?.Icon ?? 0; if (icon > 0) { var image = GetIcon(icon); - ImGui.Image(image.ImGuiHandle, new Vector2(image.Width, image.Height)); - Util.Tooltip(this.Convert(instance.Name).ToString()); + if (image != null) { + ImGui.Image(image.ImGuiHandle, new Vector2(image.Width, image.Height)); + Util.Tooltip(this.Convert(instance.Name).ToString()); + } } else { ImGui.TextUnformatted(this.Convert(instance.Name).ToString()); } @@ -557,34 +573,37 @@ namespace QuestMap { ImGui.TextUnformatted("Beast tribe"); var image = GetIcon(tribe.Icon); - ImGui.Image(image.ImGuiHandle, new Vector2(image.Width, image.Height)); - Util.Tooltip(this.Convert(tribe.Name).ToString()); + if (image != null) { + ImGui.Image(image.ImGuiHandle, new Vector2(image.Width, image.Height)); + Util.Tooltip(this.Convert(tribe.Name).ToString()); + } ImGui.Separator(); } var id = quest.RowId & 0xFFFF; - var lang = this.Plugin.Interface.ClientState.ClientLanguage switch { + var lang = this.Plugin.ClientState.ClientLanguage switch { ClientLanguage.English => Language.English, ClientLanguage.Japanese => Language.Japanese, ClientLanguage.German => Language.German, ClientLanguage.French => Language.French, _ => Language.English, }; - var path = $"quest/{id.ToString("00000").Substring(0, 3)}/{quest.Id.RawString.ToLowerInvariant()}"; + var path = $"quest/{id.ToString("00000")[..3]}/{quest.Id.RawString.ToLowerInvariant()}"; // FIXME: this is gross, but lumina caches incorrectly - this.Plugin.Interface.Data.Excel.RemoveSheetFromCache(); - var sheet = this.Plugin.Interface.Data.Excel.GetType() - .GetMethod("GetSheet", BindingFlags.Instance | BindingFlags.NonPublic) - ?.MakeGenericMethod(typeof(QuestData)) + this.Plugin.DataManager.Excel.RemoveSheetFromCache(); + var sheet = this.Plugin.DataManager.Excel.GetType() + .GetMethod("GetSheet", BindingFlags.Instance | BindingFlags.NonPublic)? // ReSharper disable once ConstantConditionalAccessQualifier - ?.Invoke(this.Plugin.Interface.Data.Excel, new object?[] { + .MakeGenericMethod(typeof(QuestData))? + // ReSharper disable once ConstantConditionalAccessQualifier + .Invoke(this.Plugin.DataManager.Excel, new object?[] { path, lang, null, }) as ExcelSheet; // default to english if reflection failed - sheet ??= this.Plugin.Interface.Data.Excel.GetSheet(path); + sheet ??= this.Plugin.DataManager.Excel.GetSheet(path); var firstData = sheet?.GetRow(0); if (firstData != null) { ImGui.PushTextWrapPos(textWrap); @@ -600,18 +619,17 @@ namespace QuestMap { } var mapLink = new MapLinkPayload( - this.Plugin.Interface.Data, level.Territory.Row, level.Map.Row, (int) (level.X * 1_000f), (int) (level.Z * 1_000f) ); - this.Plugin.Interface.Framework.Gui.OpenMapWithMapLink(mapLink); + this.Plugin.GameGui.OpenMapWithMapLink(mapLink); } - var issuer = this.Plugin.Interface.Data.GetExcelSheet().GetRow(quest.IssuerStart)?.Singular ?? "Unknown"; - var target = this.Plugin.Interface.Data.GetExcelSheet().GetRow(quest.TargetEnd)?.Singular ?? "Unknown"; + var issuer = this.Plugin.DataManager.GetExcelSheet()!.GetRow(quest.IssuerStart)?.Singular ?? "Unknown"; + var target = this.Plugin.DataManager.GetExcelSheet()!.GetRow(quest.TargetEnd)?.Singular ?? "Unknown"; ImGui.TextUnformatted(issuer); ImGui.PushFont(UiBuilder.IconFont); ImGui.SameLine(); @@ -877,7 +895,7 @@ namespace QuestMap { private static readonly byte[] NewLinePayload = { 0x02, 0x10, 0x01, 0x03 }; private SeString Convert(Lumina.Text.SeString lumina) { - var se = this.Plugin.Interface.SeStringManager.Parse(lumina.RawData.ToArray()); + var se = this.Plugin.SeStringManager.Parse(lumina.RawData.ToArray()); for (var i = 0; i < se.Payloads.Count; i++) { switch (se.Payloads[i].Type) { case PayloadType.Unknown: diff --git a/Quest Map/Quest Map.csproj b/Quest Map/Quest Map.csproj old mode 100644 new mode 100755 index 4c9758c..f77a4f5 --- a/Quest Map/Quest Map.csproj +++ b/Quest Map/Quest Map.csproj @@ -1,12 +1,14 @@ - net48 + net5-windows QuestMap 1.3.0 enable latest true + true + false @@ -30,22 +32,17 @@ $(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll False - - $(AppData)\XIVLauncher\addon\Hooks\dev\System.Memory.dll - False - - - D:\code\XivCommon\XivCommon\bin\Release\net48\XivCommon.dll - - - - + - + + + + + diff --git a/Quest Map/Quest Map.yaml b/Quest Map/Quest Map.yaml index b4b7313..e2da8fd 100644 --- a/Quest Map/Quest Map.yaml +++ b/Quest Map/Quest Map.yaml @@ -1,5 +1,6 @@ name: Quest Map author: ascclemens +punchline: Explore quests and their rewards. description: |- Explore quests and their rewards. - Search for quest names or their rewards, including instances, @@ -7,3 +8,7 @@ description: |- - See an interactive map of quest requirements and unlocks. - Open a quest info window even for quests you haven't completed. - Open quest starting locations on the map or open quests in the journal. + + Icons: treasure map by Anthony Ledoux from the Noun Project and + locked book by Anthony Ledoux from the Noun Project +repo_url: https://git.sr.ht/~jkcclemens/QuestMap diff --git a/Quest Map/QuestData.cs b/Quest Map/QuestData.cs index 1719fc0..c6ea11b 100644 --- a/Quest Map/QuestData.cs +++ b/Quest Map/QuestData.cs @@ -17,8 +17,8 @@ namespace QuestMap { public override void PopulateData(RowParser parser, GameData gameData, Language language) { base.PopulateData(parser, gameData, language); - this.Id = parser.ReadColumn(0); - this.Text = parser.ReadColumn(1); + this.Id = parser.ReadColumn(0)!; + this.Text = parser.ReadColumn(1)!; } } } diff --git a/Quest Map/Quests.cs b/Quest Map/Quests.cs index 6edd1f7..c2fd1cc 100644 --- a/Quest Map/Quests.cs +++ b/Quest Map/Quests.cs @@ -41,7 +41,7 @@ namespace QuestMap { var linkedInstances = new HashSet(); var allQuests = new Dictionary(); - foreach (var quest in this.Plugin.Interface.Data.GetExcelSheet()) { + foreach (var quest in this.Plugin.DataManager.GetExcelSheet()!) { if (quest.Name.RawString.Length == 0 || quest.RowId == 65536) { continue; } @@ -49,11 +49,11 @@ namespace QuestMap { allQuests[quest.RowId] = quest; if (quest.EmoteReward.Row != 0) { - emoteRewards[quest.RowId] = quest.EmoteReward.Value; + emoteRewards[quest.RowId] = quest.EmoteReward.Value!; } foreach (var row in quest.ItemReward0.Where(item => item != 0)) { - var item = this.Plugin.Interface.Data.GetExcelSheet().GetRow(row); + var item = this.Plugin.DataManager.GetExcelSheet()!.GetRow(row); if (item == null) { continue; } @@ -80,11 +80,11 @@ namespace QuestMap { itemRewards[quest.RowId] = rewards; } - rewards.Add(item); + rewards.Add(item!); } if (quest.ActionReward.Row != 0) { - actionRewards[quest.RowId] = quest.ActionReward.Value; + actionRewards[quest.RowId] = quest.ActionReward.Value!; } var instances = this.InstanceUnlocks(quest, linkedInstances); @@ -96,7 +96,7 @@ namespace QuestMap { } if (quest.BeastTribe.Row != 0 && !quest.IsRepeatable && quest.BeastReputationRank.Row == 0) { - beastRewards[quest.RowId] = quest.BeastTribe.Value; + beastRewards[quest.RowId] = quest.BeastTribe.Value!; } var jobReward = this.JobUnlocks(quest); @@ -259,7 +259,7 @@ namespace QuestMap { var unlocks = new HashSet(); if (quest.InstanceContentUnlock.Row != 0) { - var cfc = this.Plugin.Interface.Data.GetExcelSheet().FirstOrDefault(cfc => cfc.Content == quest.InstanceContentUnlock.Row && cfc.ContentLinkType == 1); + var cfc = this.Plugin.DataManager.GetExcelSheet()!.FirstOrDefault(cfc => cfc.Content == quest.InstanceContentUnlock.Row && cfc.ContentLinkType == 1); if (cfc != null && cfc.UnlockQuest.Row == 0) { unlocks.Add(cfc); } @@ -274,7 +274,7 @@ namespace QuestMap { // var content = this.Plugin.Interface.Data.GetExcelSheet().GetRow(key); - var cfc = this.Plugin.Interface.Data.GetExcelSheet().FirstOrDefault(cfc => cfc.Content == key && cfc.ContentLinkType == 1); + var cfc = this.Plugin.DataManager.GetExcelSheet()!.FirstOrDefault(cfc => cfc.Content == key && cfc.ContentLinkType == 1); if (cfc == null || cfc.UnlockQuest.Row != 0 || others.Contains(cfc)) { continue; } @@ -306,7 +306,7 @@ namespace QuestMap { .arg; return jobId == 0 ? null - : this.Plugin.Interface.Data.GetExcelSheet().GetRow(jobId); + : this.Plugin.DataManager.GetExcelSheet()!.GetRow(jobId); } } } diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..3bf4f3c Binary files /dev/null and b/icon.png differ diff --git a/icon.svg b/icon.svg new file mode 100755 index 0000000..6282ff8 --- /dev/null +++ b/icon.svg @@ -0,0 +1,217 @@ + + + + + Magic set RTE + + + + Magic set RTE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +