forked from liza/Questionable
Add Vanu Vanu quest pooling for talking to npcs on the island
This commit is contained in:
parent
fadc80eaff
commit
286f2c4d77
@ -12,14 +12,7 @@ namespace Questionable.Controller.Steps;
|
|||||||
|
|
||||||
internal static class QuestCleanUp
|
internal static class QuestCleanUp
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<ushort, MountConfiguration> AlliedSocietyMountConfiguration = new()
|
internal sealed class CheckAlliedSocietyMount(GameFunctions gameFunctions, AetheryteData aetheryteData, AlliedSocietyData alliedSocietyData, ILogger<CheckAlliedSocietyMount> logger) : SimpleTaskFactory
|
||||||
{
|
|
||||||
{ 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
|
|
||||||
{
|
{
|
||||||
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
|
||||||
{
|
{
|
||||||
@ -28,7 +21,7 @@ internal static class QuestCleanUp
|
|||||||
|
|
||||||
// if you are on a allied society mount
|
// if you are on a allied society mount
|
||||||
if (gameFunctions.GetMountId() is { } mountId &&
|
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);
|
logger.LogInformation("We are on a known allied society mount with id = {MountId}", mountId);
|
||||||
|
|
||||||
@ -68,6 +61,4 @@ internal static class QuestCleanUp
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed record MountConfiguration(uint IssuerDataId, EAetheryteLocation ClosestAetheryte);
|
|
||||||
}
|
}
|
||||||
|
57
Questionable/Data/AlliedSocietyData.cs
Normal file
57
Questionable/Data/AlliedSocietyData.cs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
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);
|
@ -6,6 +6,7 @@ using Dalamud.Memory;
|
|||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
|
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
@ -27,6 +28,7 @@ internal sealed unsafe class QuestFunctions
|
|||||||
private readonly QuestRegistry _questRegistry;
|
private readonly QuestRegistry _questRegistry;
|
||||||
private readonly QuestData _questData;
|
private readonly QuestData _questData;
|
||||||
private readonly AetheryteFunctions _aetheryteFunctions;
|
private readonly AetheryteFunctions _aetheryteFunctions;
|
||||||
|
private readonly AlliedSocietyData _alliedSocietyData;
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly IDataManager _dataManager;
|
private readonly IDataManager _dataManager;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
@ -36,6 +38,7 @@ internal sealed unsafe class QuestFunctions
|
|||||||
QuestRegistry questRegistry,
|
QuestRegistry questRegistry,
|
||||||
QuestData questData,
|
QuestData questData,
|
||||||
AetheryteFunctions aetheryteFunctions,
|
AetheryteFunctions aetheryteFunctions,
|
||||||
|
AlliedSocietyData alliedSocietyData,
|
||||||
Configuration configuration,
|
Configuration configuration,
|
||||||
IDataManager dataManager,
|
IDataManager dataManager,
|
||||||
IClientState clientState,
|
IClientState clientState,
|
||||||
@ -44,6 +47,7 @@ internal sealed unsafe class QuestFunctions
|
|||||||
_questRegistry = questRegistry;
|
_questRegistry = questRegistry;
|
||||||
_questData = questData;
|
_questData = questData;
|
||||||
_aetheryteFunctions = aetheryteFunctions;
|
_aetheryteFunctions = aetheryteFunctions;
|
||||||
|
_alliedSocietyData = alliedSocietyData;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_dataManager = dataManager;
|
_dataManager = dataManager;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
@ -138,7 +142,8 @@ internal sealed unsafe class QuestFunctions
|
|||||||
case 2: // leve
|
case 2: // leve
|
||||||
currentQuest = new LeveId(questManager->LeveQuests[trackedQuest.Index].LeveId);
|
currentQuest = new LeveId(questManager->LeveQuests[trackedQuest.Index].LeveId);
|
||||||
if (_questRegistry.IsKnownQuest(currentQuest))
|
if (_questRegistry.IsKnownQuest(currentQuest))
|
||||||
trackedQuests.Add((currentQuest, questManager->GetLeveQuestById(currentQuest.Value)->Sequence));
|
trackedQuests.Add((currentQuest,
|
||||||
|
questManager->GetLeveQuestById(currentQuest.Value)->Sequence));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,16 +152,65 @@ internal sealed unsafe class QuestFunctions
|
|||||||
{
|
{
|
||||||
// if we have multiple quests to turn in for an allied society, try and complete all of them
|
// if we have multiple quests to turn in for an allied society, try and complete all of them
|
||||||
var (firstTrackedQuest, firstTrackedSequence) = trackedQuests.First();
|
var (firstTrackedQuest, firstTrackedSequence) = trackedQuests.First();
|
||||||
EAlliedSociety firstTrackedAlliedSociety = GetCommonAlliedSocietyTurnIn(firstTrackedQuest);
|
EAlliedSociety firstTrackedAlliedSociety = _alliedSocietyData.GetCommonAlliedSocietyTurnIn(firstTrackedQuest);
|
||||||
if (firstTrackedAlliedSociety != EAlliedSociety.None && firstTrackedSequence == 255)
|
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)
|
||||||
|
{
|
||||||
|
if (firstTrackedSequence == 255)
|
||||||
|
{
|
||||||
|
foreach (var (quest, sequence) in alliedQuestsForSameSociety)
|
||||||
{
|
{
|
||||||
// only if the other quest isn't ready to be turned in
|
// only if the other quest isn't ready to be turned in
|
||||||
if (GetCommonAlliedSocietyTurnIn(quest) == firstTrackedAlliedSociety && sequence != 255)
|
if (sequence != 255)
|
||||||
return (quest, sequence);
|
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 (firstTrackedQuest, firstTrackedSequence);
|
return (firstTrackedQuest, firstTrackedSequence);
|
||||||
}
|
}
|
||||||
@ -237,20 +291,24 @@ internal sealed unsafe class QuestFunctions
|
|||||||
return (currentQuest, QuestManager.GetQuestSequence(currentQuest.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 &&
|
||||||
return questId.Value switch
|
battleChara->Mount.MountId != 0 &&
|
||||||
{
|
_alliedSocietyData.Mounts.ContainsKey(battleChara->Mount.MountId);
|
||||||
>= 2171 and <= 2200 => EAlliedSociety.VanuVanu,
|
|
||||||
>= 2261 and <= 2280 => EAlliedSociety.Vath,
|
|
||||||
>= 5199 and <= 5226 => EAlliedSociety.Pelupelu,
|
|
||||||
_ => EAlliedSociety.None,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return EAlliedSociety.None;
|
private bool IsInteractStep(ElementId questId, byte sequence, uint[] dataIds)
|
||||||
|
{
|
||||||
|
if (_questRegistry.TryGetQuest(questId, out var quest))
|
||||||
|
{
|
||||||
|
QuestStep? firstStepOfSequence = quest.FindSequence(sequence)?.FindStep(0);
|
||||||
|
return firstStepOfSequence is { InteractionType: EInteractionType.Interact, DataId: { } dataId } &&
|
||||||
|
dataIds.Contains(dataId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuestProgressInfo? GetQuestProgressInfo(ElementId elementId)
|
public QuestProgressInfo? GetQuestProgressInfo(ElementId elementId)
|
||||||
|
@ -115,6 +115,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
|||||||
|
|
||||||
serviceCollection.AddSingleton<AetherCurrentData>();
|
serviceCollection.AddSingleton<AetherCurrentData>();
|
||||||
serviceCollection.AddSingleton<AetheryteData>();
|
serviceCollection.AddSingleton<AetheryteData>();
|
||||||
|
serviceCollection.AddSingleton<AlliedSocietyData>();
|
||||||
serviceCollection.AddSingleton<GatheringData>();
|
serviceCollection.AddSingleton<GatheringData>();
|
||||||
serviceCollection.AddSingleton<LeveData>();
|
serviceCollection.AddSingleton<LeveData>();
|
||||||
serviceCollection.AddSingleton<JournalData>();
|
serviceCollection.AddSingleton<JournalData>();
|
||||||
|
Loading…
Reference in New Issue
Block a user