internal static class QuestCleanUp
{
- private static readonly Dictionary<ushort, MountConfiguration> AlliedSocietyMountConfiguration = new()
- {
- { 66, new(1016093, EAetheryteLocation.SeaOfCloudsOkZundu) },
- { 79, new(1017031, EAetheryteLocation.DravanianForelandsAnyxTrine) },
- { 369, new(1051798, EAetheryteLocation.KozamaukaDockPoga) },
- };
-
- internal sealed class CheckAlliedSocietyMount(GameFunctions gameFunctions, AetheryteData aetheryteData, ILogger<CheckAlliedSocietyMount> logger) : SimpleTaskFactory
+ internal sealed class CheckAlliedSocietyMount(GameFunctions gameFunctions, AetheryteData aetheryteData, AlliedSocietyData alliedSocietyData, ILogger<CheckAlliedSocietyMount> logger) : SimpleTaskFactory
{
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{
// if you are on a allied society mount
if (gameFunctions.GetMountId() is { } mountId &&
- AlliedSocietyMountConfiguration.TryGetValue(mountId, out var mountConfiguration))
+ alliedSocietyData.Mounts.TryGetValue(mountId, out var mountConfiguration))
{
logger.LogInformation("We are on a known allied society mount with id = {MountId}", mountId);
return null;
}
}
-
- private sealed record MountConfiguration(uint IssuerDataId, EAetheryteLocation ClosestAetheryte);
}
--- /dev/null
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
+using Questionable.Model;
+using Questionable.Model.Common;
+using Questionable.Model.Questing;
+
+namespace Questionable.Data;
+
+[SuppressMessage("Performance", "CA1822")]
+internal sealed class AlliedSocietyData
+{
+ public ReadOnlyDictionary<ushort, AlliedSocietyMountConfiguration> Mounts { get; } =
+ new Dictionary<ushort, AlliedSocietyMountConfiguration>
+ {
+ { 66, new(1016093, EAetheryteLocation.SeaOfCloudsOkZundu) },
+ { 79, new(1017031, EAetheryteLocation.DravanianForelandsAnyxTrine) },
+ { 369, new(1051798, EAetheryteLocation.KozamaukaDockPoga) },
+ }.AsReadOnly();
+
+ public EAlliedSociety GetCommonAlliedSocietyTurnIn(ElementId elementId)
+ {
+ if (elementId is QuestId questId)
+ {
+ return questId.Value switch
+ {
+ >= 2171 and <= 2200 => EAlliedSociety.VanuVanu,
+ >= 2261 and <= 2280 => EAlliedSociety.Vath,
+ >= 5199 and <= 5226 => EAlliedSociety.Pelupelu,
+ _ => EAlliedSociety.None,
+ };
+ }
+
+ return EAlliedSociety.None;
+ }
+
+ public void GetCommonAlliedSocietyNpcs(EAlliedSociety alliedSociety, out uint[] normalNpcs, out uint[] mountNpcs)
+ {
+ if (alliedSociety == EAlliedSociety.VanuVanu)
+ {
+ normalNpcs = [1016088, 1016091, 1016092];
+ mountNpcs = [1016093];
+ }
+ else if (alliedSociety == EAlliedSociety.Vath)
+ {
+ normalNpcs = [];
+ mountNpcs = [1017031];
+ }
+ else
+ {
+ normalNpcs = [];
+ mountNpcs = [];
+ }
+ }
+}
+
+public sealed record AlliedSocietyMountConfiguration(uint IssuerDataId, EAetheryteLocation ClosestAetheryte);
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
using FFXIVClientStructs.FFXIV.Client.Game;
+using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI;
private readonly QuestRegistry _questRegistry;
private readonly QuestData _questData;
private readonly AetheryteFunctions _aetheryteFunctions;
+ private readonly AlliedSocietyData _alliedSocietyData;
private readonly Configuration _configuration;
private readonly IDataManager _dataManager;
private readonly IClientState _clientState;
QuestRegistry questRegistry,
QuestData questData,
AetheryteFunctions aetheryteFunctions,
+ AlliedSocietyData alliedSocietyData,
Configuration configuration,
IDataManager dataManager,
IClientState clientState,
_questRegistry = questRegistry;
_questData = questData;
_aetheryteFunctions = aetheryteFunctions;
+ _alliedSocietyData = alliedSocietyData;
_configuration = configuration;
_dataManager = dataManager;
_clientState = clientState;
case 2: // leve
currentQuest = new LeveId(questManager->LeveQuests[trackedQuest.Index].LeveId);
if (_questRegistry.IsKnownQuest(currentQuest))
- trackedQuests.Add((currentQuest, questManager->GetLeveQuestById(currentQuest.Value)->Sequence));
+ trackedQuests.Add((currentQuest,
+ questManager->GetLeveQuestById(currentQuest.Value)->Sequence));
break;
}
}
{
// if we have multiple quests to turn in for an allied society, try and complete all of them
var (firstTrackedQuest, firstTrackedSequence) = trackedQuests.First();
- EAlliedSociety firstTrackedAlliedSociety = GetCommonAlliedSocietyTurnIn(firstTrackedQuest);
- if (firstTrackedAlliedSociety != EAlliedSociety.None && firstTrackedSequence == 255)
+ EAlliedSociety firstTrackedAlliedSociety = _alliedSocietyData.GetCommonAlliedSocietyTurnIn(firstTrackedQuest);
+ if (firstTrackedAlliedSociety != EAlliedSociety.None)
{
- foreach (var (quest, sequence) in trackedQuests.Skip(1))
+ var alliedQuestsForSameSociety = trackedQuests.Skip(1)
+ .Where(quest => _alliedSocietyData.GetCommonAlliedSocietyTurnIn(quest.Quest) == firstTrackedAlliedSociety)
+ .ToList();
+ if (alliedQuestsForSameSociety.Count > 0)
{
- // only if the other quest isn't ready to be turned in
- if (GetCommonAlliedSocietyTurnIn(quest) == firstTrackedAlliedSociety && sequence != 255)
- return (quest, sequence);
+ if (firstTrackedSequence == 255)
+ {
+ foreach (var (quest, sequence) in alliedQuestsForSameSociety)
+ {
+ // only if the other quest isn't ready to be turned in
+ if (sequence != 255)
+ return (quest, sequence);
+ }
+ }
+ else if (!IsOnAlliedSocietyMount())
+ {
+ // a few of the vanu quests require you to talk to one of the npcs near the issuer, so we
+ // give priority to those
+
+ // also include the first quest in the list for those
+ alliedQuestsForSameSociety.Insert(0, (firstTrackedQuest, firstTrackedSequence));
+
+ _alliedSocietyData.GetCommonAlliedSocietyNpcs(firstTrackedAlliedSociety, out uint[]? normalNpcs,
+ out uint[]? mountNpcs);
+
+ if (normalNpcs.Length > 0)
+ {
+ var talkToNormalNpcs = alliedQuestsForSameSociety
+ .Where(x => x.Sequence < 255)
+ .Where(x => IsInteractStep(x.Quest, x.Sequence, normalNpcs))
+ .Cast<(ElementId, byte)?>()
+ .FirstOrDefault();
+ if (talkToNormalNpcs != null)
+ return talkToNormalNpcs.Value;
+ }
+
+ /*
+ * TODO: If you have e.g. a mount quest in the middle of 3, it should temporarily make you
+ * do that quest first, even if it isn't the first in the list. Otherwise, the logic
+ * here won't make much sense.
+ *
+ * TODO: This also won't work if two or three daily quests use a mount.
+ if (mountNpcs.Length > 0)
+ {
+ var talkToMountNpc = alliedQuestsForSameSociety
+ .Where(x => x.Sequence < 255)
+ .Where(x => IsInteractStep(x.Quest, x.Sequence, mountNpcs))
+ .Cast<(ElementId, byte)?>()
+ .FirstOrDefault();
+ if (talkToMountNpc != null)
+ return talkToMountNpc.Value;
+ }
+ */
+ }
}
}
return (currentQuest, QuestManager.GetQuestSequence(currentQuest.Value));
}
- private static EAlliedSociety GetCommonAlliedSocietyTurnIn(ElementId elementId)
+ private bool IsOnAlliedSocietyMount()
{
- if (elementId is QuestId questId)
+ BattleChara* battleChara = (BattleChara*)(_clientState.LocalPlayer?.Address ?? 0);
+ return battleChara != null &&
+ battleChara->Mount.MountId != 0 &&
+ _alliedSocietyData.Mounts.ContainsKey(battleChara->Mount.MountId);
+ }
+
+ private bool IsInteractStep(ElementId questId, byte sequence, uint[] dataIds)
+ {
+ if (_questRegistry.TryGetQuest(questId, out var quest))
{
- return questId.Value switch
- {
- >= 2171 and <= 2200 => EAlliedSociety.VanuVanu,
- >= 2261 and <= 2280 => EAlliedSociety.Vath,
- >= 5199 and <= 5226 => EAlliedSociety.Pelupelu,
- _ => EAlliedSociety.None,
- };
+ QuestStep? firstStepOfSequence = quest.FindSequence(sequence)?.FindStep(0);
+ return firstStepOfSequence is { InteractionType: EInteractionType.Interact, DataId: { } dataId } &&
+ dataIds.Contains(dataId);
}
- return EAlliedSociety.None;
+ return false;
}
public QuestProgressInfo? GetQuestProgressInfo(ElementId elementId)