+++ /dev/null
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Questionable.Model.Questing;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-using static Questionable.QuestPathGenerator.RoslynShortcuts;
-
-namespace Questionable.QuestPathGenerator.RoslynElements;
-
-internal static class LeveIdExtensions
-{
- public static ExpressionSyntax ToExpressionSyntax(this LeveId leveId)
- {
- return ObjectCreationExpression(
- IdentifierName(nameof(LeveId)))
- .WithArgumentList(
- ArgumentList(
- SingletonSeparatedList(
- Argument(LiteralValue(leveId.Value)))));
- }
-}
float f => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(f)),
QuestStep step => step.ToExpressionSyntax(),
QuestId questId => questId.ToExpressionSyntax(),
- LeveId leveId => leveId.ToExpressionSyntax(),
SatisfactionSupplyNpcId satisfactionSupplyNpcId => satisfactionSupplyNpcId.ToExpressionSyntax(),
AlliedSocietyDailyId alliedSocietyDailyId => alliedSocietyDailyId.ToExpressionSyntax(),
Vector3 vector => vector.ToExpressionSyntax(),
+++ /dev/null
-{
- "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
- "Author": "liza",
- "QuestSequence": [
- {
- "Sequence": 1,
- "Steps": [
- {
- "Position": {
- "X": -213.94563,
- "Y": 84.02199,
- "Z": 43.544315
- },
- "TerritoryId": 814,
- "InteractionType": "InitiateLeve",
- "AetheryteShortcut": "Kholusia - Wright",
- "Fly": true,
- "SkipConditions": {
- "AetheryteShortcutIf": {
- "InSameTerritory": true
- }
- }
- },
- {
- "TerritoryId": 814,
- "InteractionType": "Gather",
- "ItemsToGather": [
- {
- "ItemId": 2002785,
- "AlternativeItemId": 2002786,
- "ItemCount": 999
- }
- ]
- }
- ]
- }
- ]
-}
+++ /dev/null
-{
- "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
- "Author": "liza",
- "QuestSequence": [
- {
- "Sequence": 1,
- "Steps": [
- {
- "Position": {
- "X": -506.1484,
- "Y": -22.671598,
- "Z": -116.16144
- },
- "TerritoryId": 956,
- "InteractionType": "InitiateLeve",
- "AetheryteShortcut": "Labyrinthos - Aporia",
- "Fly": true,
- "SkipConditions": {
- "AetheryteShortcutIf": {
- "InSameTerritory": true
- }
- }
- },
- {
- "TerritoryId": 956,
- "InteractionType": "Gather",
- "ItemsToGather": [
- {
- "ItemId": 2003172,
- "AlternativeItemId": 2003173,
- "ItemCount": 999
- }
- ]
- }
- ]
- }
- ]
-}
+++ /dev/null
-{
- "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
- "Author": "liza",
- "QuestSequence": [
- {
- "Sequence": 1,
- "Steps": [
- {
- "Position": {
- "X": 767.05066,
- "Y": 24.645323,
- "Z": -561.67883
- },
- "TerritoryId": 1188,
- "InteractionType": "InitiateLeve",
- "AetheryteShortcut": "Kozama'uka - Ok'hanu",
- "Fly": true,
- "SkipConditions": {
- "AetheryteShortcutIf": {
- "InSameTerritory": true
- }
- }
- },
- {
- "TerritoryId": 1188,
- "InteractionType": "Gather",
- "ItemsToGather": [
- {
- "ItemId": 2003516,
- "AlternativeItemId": 2003517,
- "ItemCount": 999
- }
- ]
- }
- ]
- }
- ]
-}
+++ /dev/null
-{
- "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
- "Author": "liza",
- "QuestSequence": [
- {
- "Sequence": 1,
- "Steps": [
- {
- "Position": {
- "X": 518.05945,
- "Y": 19.47163,
- "Z": -335.71478
- },
- "TerritoryId": 1189,
- "InteractionType": "InitiateLeve",
- "AetheryteShortcut": "Yak T'el - Iq Br'aax",
- "Fly": true,
- "SkipConditions": {
- "AetheryteShortcutIf": {
- "InSameTerritory": true
- }
- }
- },
- {
- "TerritoryId": 1189,
- "InteractionType": "Gather",
- "ItemsToGather": [
- {
- "ItemId": 2003529,
- "AlternativeItemId": 2003530,
- "ItemCount": 999
- }
- ]
- }
- ]
- }
- ]
-}
+++ /dev/null
-{
- "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
- "Author": "liza",
- "QuestSequence": [
- {
- "Sequence": 1,
- "Steps": [
- {
- "Position": {
- "X": 228.24704,
- "Y": -91.93331,
- "Z": -229.72751
- },
- "TerritoryId": 1187,
- "InteractionType": "InitiateLeve",
- "AetheryteShortcut": "Urqopacha - Wachunpelo",
- "Fly": true,
- "SkipConditions": {
- "AetheryteShortcutIf": {
- "InSameTerritory": true
- }
- }
- },
- {
- "TerritoryId": 1187,
- "InteractionType": "Gather",
- "ItemsToGather": [
- {
- "ItemId": 2003539,
- "AlternativeItemId": 2003540,
- "ItemCount": 999
- }
- ]
- }
- ]
- }
- ]
-}
+++ /dev/null
-{
- "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
- "Author": "liza",
- "QuestSequence": [
- {
- "Sequence": 1,
- "Steps": [
- {
- "Position": {
- "X": 664.32874,
- "Y": 24.373428,
- "Z": -85.7219
- },
- "TerritoryId": 1189,
- "InteractionType": "InitiateLeve",
- "AetheryteShortcut": "Yak T'el - Mamook",
- "Fly": true,
- "SkipConditions": {
- "AetheryteShortcutIf": {
- "InSameTerritory": true
- }
- }
- },
- {
- "TerritoryId": 1189,
- "InteractionType": "Gather",
- "ItemsToGather": [
- {
- "ItemId": 2003552,
- "AlternativeItemId": 2003553,
- "ItemCount": 999
- }
- ]
- }
- ]
- }
- ]
-}
{ EInteractionType.Instruction, "Instruction" },
{ EInteractionType.AcceptQuest, "AcceptQuest" },
{ EInteractionType.CompleteQuest, "CompleteQuest" },
- { EInteractionType.InitiateLeve, "InitiateLeve" },
};
}
AcceptQuest,
CompleteQuest,
- AcceptLeve,
- InitiateLeve,
- CompleteLeve,
}
public static ElementId FromString(string value)
{
- if (value.StartsWith("L"))
- return new LeveId(ushort.Parse(value.Substring(1), CultureInfo.InvariantCulture));
- else if (value.StartsWith("S"))
+ if (value.StartsWith("S"))
return new SatisfactionSupplyNpcId(ushort.Parse(value.Substring(1), CultureInfo.InvariantCulture));
else if (value.StartsWith("A"))
{
}
}
-public sealed class LeveId(ushort value) : ElementId(value)
-{
- public override string ToString()
- {
- return "L" + Value.ToString(CultureInfo.InvariantCulture);
- }
-}
-
public sealed class SatisfactionSupplyNpcId(ushort value) : ElementId(value)
{
public override string ToString()
{
List<DialogueChoiceInfo> dialogueChoices = [];
- // levequest choices have some vague sort of priority
- if (_questController.HasCurrentTaskExecutorMatching<Interact.DoInteract>(out var interact) &&
- interact.Quest != null &&
- interact.InteractionType is EInteractionType.AcceptLeve or EInteractionType.CompleteLeve)
- {
- if (interact.InteractionType == EInteractionType.AcceptLeve)
- {
- dialogueChoices.Add(new DialogueChoiceInfo(interact.Quest,
- new DialogueChoice
- {
- Type = EDialogChoiceType.List,
- ExcelSheet = "leve/GuildleveAssignment",
- Prompt = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_TITLE"),
- Answer = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_01"),
- }));
- interact.InteractionType = EInteractionType.None;
- }
- else if (interact.InteractionType == EInteractionType.CompleteLeve)
- {
- dialogueChoices.Add(new DialogueChoiceInfo(interact.Quest,
- new DialogueChoice
- {
- Type = EDialogChoiceType.List,
- ExcelSheet = "leve/GuildleveAssignment",
- Prompt = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_TITLE"),
- Answer = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_REWARD"),
- }));
- interact.InteractionType = EInteractionType.None;
- }
- }
-
var currentQuest = _questController.SimulatedQuest ??
_questController.GatheringQuest ??
_questController.StartedQuest;
}
}
}
-
- if ((_questController.IsRunning || _questController.WasLastTaskUpdateWithin(TimeSpan.FromSeconds(5)))
- && _questController.NextQuest == null)
- {
- // make sure to always close the leve dialogue
- if (_questData.GetAllByIssuerDataId(target.DataId).Any(x => x.QuestId is LeveId))
- {
- _logger.LogInformation("Adding close leve dialogue as option");
- dialogueChoices.Add(new DialogueChoiceInfo(null,
- new DialogueChoice
- {
- Type = EDialogChoiceType.List,
- ExcelSheet = "leve/GuildleveAssignment",
- Prompt = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_TITLE"),
- Answer = new ExcelRef("TEXT_GUILDLEVEASSIGNMENT_SELECT_MENU_07"),
- }));
- }
- }
}
if (dialogueChoices.Count == 0)
return true;
}
- if (currentQuest.Quest.Id is LeveId)
- {
- var dialogueChoice = new DialogueChoice
- {
- Type = EDialogChoiceType.YesNo,
- ExcelSheet = "Addon",
- Prompt = new ExcelRef(608),
- Yes = true
- };
-
- if (HandleDefaultYesNo(addonSelectYesno, quest, null, [dialogueChoice], actualPrompt))
- return true;
- }
-
if (HandleTravelYesNo(addonSelectYesno, currentQuest, actualPrompt))
return true;
+++ /dev/null
-using System;
-using System.Linq;
-using Dalamud.Game.Addon.Lifecycle;
-using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
-using Dalamud.Game.ClientState.Objects;
-using Dalamud.Plugin.Services;
-using FFXIVClientStructs.FFXIV.Client.UI;
-using FFXIVClientStructs.FFXIV.Client.UI.Agent;
-using FFXIVClientStructs.FFXIV.Component.GUI;
-using LLib.GameUI;
-using Microsoft.Extensions.Logging;
-using Questionable.Data;
-using Questionable.Functions;
-using Questionable.Model.Questing;
-using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
-
-namespace Questionable.Controller.GameUi;
-
-internal sealed class LeveUiController : IDisposable
-{
- private readonly QuestController _questController;
- private readonly QuestData _questData;
- private readonly QuestFunctions _questFunctions;
- private readonly IAddonLifecycle _addonLifecycle;
- private readonly IGameGui _gameGui;
- private readonly ITargetManager _targetManager;
- private readonly IFramework _framework;
- private readonly ILogger<LeveUiController> _logger;
-
- public LeveUiController(QuestController questController, QuestData questData, QuestFunctions questFunctions,
- IAddonLifecycle addonLifecycle, IGameGui gameGui, ITargetManager targetManager, IFramework framework,
- ILogger<LeveUiController> logger)
- {
- _questController = questController;
- _questData = questData;
- _questFunctions = questFunctions;
- _addonLifecycle = addonLifecycle;
- _gameGui = gameGui;
- _targetManager = targetManager;
- _framework = framework;
- _logger = logger;
-
- _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "JournalResult", JournalResultPostSetup);
- _addonLifecycle.RegisterListener(AddonEvent.PostSetup, "GuildLeve", GuildLevePostSetup);
- }
-
- private bool ShouldHandleUiInteractions => _questController.IsRunning;
-
- private unsafe void JournalResultPostSetup(AddonEvent type, AddonArgs args)
- {
- if (!ShouldHandleUiInteractions)
- return;
-
- _logger.LogInformation("Checking for quest name of journal result");
- AddonJournalResult* addon = (AddonJournalResult*)args.Addon;
-
- string questName = addon->AtkTextNode250->NodeText.ToString();
- if (_questController.CurrentQuest is { Quest.Id: LeveId } &&
- GameFunctions.GameStringEquals(_questController.CurrentQuest.Quest.Info.Name, questName))
- {
- _logger.LogInformation("JournalResult has the current leve, auto-accepting it");
- addon->FireCallbackInt(0);
- }
- else if (_targetManager.Target is { } target)
- {
- var issuedLeves = _questData.GetAllByIssuerDataId(target.DataId)
- .Where(x => x.QuestId is LeveId)
- .ToList();
-
- if (issuedLeves.Any(x => GameFunctions.GameStringEquals(x.Name, questName)))
- {
- _logger.LogInformation(
- "JournalResult has a leve but not the one we're currently on, auto-declining it");
- addon->FireCallbackInt(1);
- }
- }
- }
-
- private void GuildLevePostSetup(AddonEvent type, AddonArgs args)
- {
- var target = _targetManager.Target;
- if (target == null)
- return;
-
- if (_questController is { IsRunning: true, NextQuest: { Quest.Id: LeveId } nextQuest } &&
- _questFunctions.IsReadyToAcceptQuest(nextQuest.Quest.Id))
- {
- /*
- var addon = (AddonGuildLeve*)args.Addon;
- var atkValues = addon->AtkValues;
-
- var availableLeves = _questData.GetAllByIssuerDataId(target.DataId);
- List<(int, IQuestInfo)> offeredLeves = [];
- for (int i = 0; i <= 20; ++i) // 3 leves per group, 1 label for group
- {
- string? leveName = atkValues[626 + i * 2].ReadAtkString();
- if (leveName == null)
- continue;
-
- var questInfo = availableLeves.FirstOrDefault(x => GameFunctions.GameStringEquals(x.Name, leveName));
- if (questInfo == null)
- continue;
-
- offeredLeves.Add((i, questInfo));
-
- }
-
- foreach (var (i, questInfo) in offeredLeves)
- _logger.LogInformation("Leve {Index} = {Id}, {Name}", i, questInfo.QuestId, questInfo.Name);
- */
-
- _framework.RunOnTick(() => AcceptLeveOrWait(nextQuest), TimeSpan.FromMilliseconds(100));
- }
- }
-
- private unsafe void AcceptLeveOrWait(QuestController.QuestProgress nextQuest, int counter = 0)
- {
- var agent = UIModule.Instance()->GetAgentModule()->GetAgentByInternalId(AgentId.LeveQuest);
- if (agent->IsAgentActive() &&
- _gameGui.TryGetAddonByName("GuildLeve", out AddonGuildLeve* addonGuildLeve) &&
- LAddon.IsAddonReady(&addonGuildLeve->AtkUnitBase) &&
- _gameGui.TryGetAddonByName("JournalDetail", out AtkUnitBase* addonJournalDetail) &&
- LAddon.IsAddonReady(addonJournalDetail))
- {
- AcceptLeve(agent, addonGuildLeve, nextQuest);
- }
- else if (counter >= 10)
- _logger.LogWarning("Unable to accept leve?");
- else
- _framework.RunOnTick(() => AcceptLeveOrWait(nextQuest, counter + 1), TimeSpan.FromMilliseconds(100));
- }
-
- private unsafe void AcceptLeve(AgentInterface* agent, AddonGuildLeve* addon,
- QuestController.QuestProgress nextQuest)
- {
- _questController.SetPendingQuest(nextQuest);
- _questController.SetNextQuest(null);
-
- var returnValue = stackalloc AtkValue[1];
- var selectQuest = stackalloc AtkValue[]
- {
- new() { Type = ValueType.Int, Int = 3 },
- new() { Type = ValueType.UInt, UInt = nextQuest.Quest.Id.Value }
- };
- agent->ReceiveEvent(returnValue, selectQuest, 2, 0);
- addon->Close(true);
- }
-
- public void Dispose()
- {
- _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "GuildLeve", GuildLevePostSetup);
- _addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "JournalResult", JournalResultPostSetup);
- }
-}
private readonly QuestValidator _questValidator;
private readonly JsonSchemaValidator _jsonSchemaValidator;
private readonly ILogger<QuestRegistry> _logger;
- private readonly LeveData _leveData;
private readonly TerritoryData _territoryData;
private readonly IChatGui _chatGui;
QuestValidator questValidator,
JsonSchemaValidator jsonSchemaValidator,
ILogger<QuestRegistry> logger,
- LeveData leveData,
TerritoryData territoryData,
IChatGui chatGui)
{
_questValidator = questValidator;
_jsonSchemaValidator = jsonSchemaValidator;
_logger = logger;
- _leveData = leveData;
_territoryData = territoryData;
_chatGui = chatGui;
_reloadDataIpc = _pluginInterface.GetIpcProvider<object>("Questionable.ReloadData");
try
{
var questInfo = _questData.GetQuestInfo(questId);
- if (questInfo is LeveInfo leveInfo)
- _leveData.AddQuestSteps(leveInfo, questRoot);
Quest quest = new()
{
Id = questId,
var questRoot = questNode.Deserialize<QuestRoot>()!;
var questInfo = _questData.GetQuestInfo(questId);
- if (questInfo is LeveInfo leveInfo)
- _leveData.AddQuestSteps(leveInfo, questRoot);
Quest quest = new Quest
{
Id = questId,
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{
if (step.InteractionType is EInteractionType.AcceptQuest or EInteractionType.CompleteQuest
- or EInteractionType.AcceptLeve or EInteractionType.CompleteLeve
or EInteractionType.SinglePlayerDuty)
{
if (step.Emote != null)
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using Dalamud.Game.ClientState.Conditions;
-using Dalamud.Plugin.Services;
-using FFXIVClientStructs.FFXIV.Client.Game.Event;
-using FFXIVClientStructs.FFXIV.Client.Game.UI;
-using FFXIVClientStructs.FFXIV.Client.UI.Agent;
-using FFXIVClientStructs.FFXIV.Component.GUI;
-using LLib.GameUI;
-using Questionable.Controller.Steps.Common;
-using Questionable.Model;
-using Questionable.Model.Questing;
-using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
-
-namespace Questionable.Controller.Steps.Leves;
-
-internal static class InitiateLeve
-{
- internal sealed class Factory(ICondition condition) : ITaskFactory
- {
- public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
- {
- if (step.InteractionType != EInteractionType.InitiateLeve)
- yield break;
-
- yield return new SkipInitiateIfActive(quest.Id);
- yield return new OpenJournal(quest.Id);
- yield return new Initiate(quest.Id);
- yield return new SelectDifficulty();
- yield return new WaitCondition.Task(() => condition[ConditionFlag.BoundByDuty], "Wait(BoundByDuty)");
- }
- }
-
- internal sealed record SkipInitiateIfActive(ElementId ElementId) : ITask
- {
- public override string ToString() => $"CheckIfAlreadyActive({ElementId})";
- }
-
- internal sealed unsafe class SkipInitiateIfActiveExecutor : TaskExecutor<SkipInitiateIfActive>
- {
- protected override bool Start() => true;
-
- public override ETaskResult Update()
- {
- var director = UIState.Instance()->DirectorTodo.Director;
- if (director != null &&
- director->Info.EventId.ContentId == EventHandlerContent.GatheringLeveDirector &&
- director->ContentId == Task.ElementId.Value)
- return ETaskResult.SkipRemainingTasksForStep;
-
- return ETaskResult.TaskComplete;
- }
-
- public override bool ShouldInterruptOnDamage() => false;
- }
-
- internal sealed record OpenJournal(ElementId ElementId) : ITask
- {
- public uint QuestType => ElementId is LeveId ? 2u : 1u;
- public override string ToString() => $"OpenJournal({ElementId})";
- }
-
- internal sealed unsafe class OpenJournalExecutor : TaskExecutor<OpenJournal>
- {
- private DateTime _openedAt = DateTime.MinValue;
-
- protected override bool Start()
- {
- AgentQuestJournal.Instance()->OpenForQuest(Task.ElementId.Value, Task.QuestType);
- _openedAt = DateTime.Now;
- return true;
- }
-
- public override ETaskResult Update()
- {
- AgentQuestJournal* agentQuestJournal = AgentQuestJournal.Instance();
- if (agentQuestJournal->IsAgentActive() &&
- agentQuestJournal->SelectedQuestId == Task.ElementId.Value &&
- agentQuestJournal->SelectedQuestType == Task.QuestType)
- return ETaskResult.TaskComplete;
-
- if (DateTime.Now > _openedAt.AddSeconds(3))
- {
- AgentQuestJournal.Instance()->OpenForQuest(Task.ElementId.Value, Task.QuestType);
- _openedAt = DateTime.Now;
- }
-
- return ETaskResult.StillRunning;
- }
-
- public override bool ShouldInterruptOnDamage() => false;
- }
-
- internal sealed record Initiate(ElementId ElementId) : ITask
- {
- public override string ToString() => $"InitiateLeve({ElementId})";
- }
-
- internal sealed unsafe class InitiateExecutor(IGameGui gameGui) : TaskExecutor<Initiate>
- {
- protected override bool Start() => true;
-
- public override ETaskResult Update()
- {
- if (gameGui.TryGetAddonByName("JournalDetail", out AtkUnitBase* addonJournalDetail))
- {
- var pickQuest = stackalloc AtkValue[]
- {
- new() { Type = ValueType.Int, Int = 4 },
- new() { Type = ValueType.UInt, Int = Task.ElementId.Value }
- };
- addonJournalDetail->FireCallback(2, pickQuest);
- return ETaskResult.TaskComplete;
- }
-
- return ETaskResult.StillRunning;
- }
-
- public override bool ShouldInterruptOnDamage() => false;
- }
-
- internal sealed class SelectDifficulty : ITask
- {
- public override string ToString() => "SelectLeveDifficulty";
- }
-
- internal sealed unsafe class SelectDifficultyExecutor(IGameGui gameGui) : TaskExecutor<SelectDifficulty>
- {
- protected override bool Start() => true;
-
- public override ETaskResult Update()
- {
- if (gameGui.TryGetAddonByName("GuildLeveDifficulty", out AtkUnitBase* addon))
- {
- // atkvalues: 1 → default difficulty, 2 → min, 3 → max
- var pickDifficulty = stackalloc AtkValue[]
- {
- new() { Type = ValueType.Int, Int = 0 },
- new() { Type = ValueType.Int, Int = addon->AtkValues[1].Int }
- };
- addon->FireCallback(2, pickDifficulty, true);
- return ETaskResult.TaskComplete;
- }
-
- return ETaskResult.StillRunning;
- }
-
- public override bool ShouldInterruptOnDamage() => false;
- }
-}
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using FFXIVClientStructs.FFXIV.Common.Math;
-using LLib.GameData;
-using Questionable.Model;
-using Questionable.Model.Common;
-using Questionable.Model.Questing;
-
-namespace Questionable.Data;
-
-internal sealed class LeveData
-{
- private static readonly List<LeveStepData> Leves =
- [
- new(
- aetheryteLocation: EAetheryteLocation.Crystarium,
- aethernetShortcut: new AethernetShortcut{From = EAetheryteLocation.Crystarium, To = EAetheryteLocation.CrystariumCrystallineMean},
- issuerDataId: 1027847,
- issuerPosition: new(-73.94349f, 19.999794f, -110.86395f)),
- new(
- aetheryteLocation: EAetheryteLocation.OldSharlayan,
- aethernetShortcut: new AethernetShortcut
- { From = EAetheryteLocation.OldSharlayan, To = EAetheryteLocation.OldSharlayanScholarsHarbor },
- issuerDataId: 1037263,
- issuerPosition: new(45.818386f, -15.646993f, 109.40509f)),
- new(aetheryteLocation: EAetheryteLocation.Tuliyollal,
- aethernetShortcut: null,
- issuerDataId: 1048390,
- issuerPosition: new(15.243713f, -14.000001f, 85.83191f)),
- ];
-
- private readonly AetheryteData _aetheryteData;
-
- public LeveData(AetheryteData aetheryteData)
- {
- _aetheryteData = aetheryteData;
- }
-
- public void AddQuestSteps(LeveInfo leveInfo, QuestRoot questRoot)
- {
- LeveStepData leveStepData = Leves.SingleOrDefault(x => x.IssuerDataId == leveInfo.IssuerDataId)
- ?? throw new InvalidOperationException(
- $"No leve location for issuer data id {leveInfo.IssuerDataId} found");
-
- QuestSequence? startSequence = questRoot.QuestSequence.FirstOrDefault(x => x.Sequence == 0);
- if (startSequence == null)
- {
- questRoot.QuestSequence.Add(new QuestSequence
- {
- Sequence = 0,
- Steps =
- [
- new QuestStep
- {
- DataId = leveStepData.IssuerDataId,
- Position = leveStepData.IssuerPosition,
- TerritoryId = _aetheryteData.TerritoryIds[leveStepData.AetheryteLocation],
- InteractionType = EInteractionType.AcceptLeve,
- AetheryteShortcut = leveStepData.AetheryteLocation,
- AethernetShortcut = leveStepData.AethernetShortcut,
- SkipConditions = new()
- {
- AetheryteShortcutIf = new()
- {
- InSameTerritory = true,
- }
- }
- }
- ]
- });
- }
-
- QuestSequence? endSequence = questRoot.QuestSequence.FirstOrDefault(x => x.Sequence == 255);
- if (endSequence == null)
- {
- questRoot.QuestSequence.Add(new QuestSequence
- {
- Sequence = 255,
- Steps =
- [
- new QuestStep
- {
- DataId = leveStepData.GetTurnInDataId(leveInfo),
- Position = leveStepData.GetTurnInPosition(leveInfo),
- TerritoryId = _aetheryteData.TerritoryIds[leveStepData.AetheryteLocation],
- InteractionType = EInteractionType.CompleteLeve,
- AetheryteShortcut = leveStepData.AetheryteLocation,
- AethernetShortcut = leveStepData.AethernetShortcut,
- SkipConditions = new()
- {
- AetheryteShortcutIf = new()
- {
- InSameTerritory = true,
- }
- }
- }
- ]
- });
- }
- }
-
- private sealed class LeveStepData
- {
- private readonly uint? _turnInDataId;
- private readonly Vector3? _turnInPosition;
- private readonly uint? _gathererTurnInDataId;
- private readonly Vector3? _gathererTurnInPosition;
- private readonly uint? _crafterTurnInDataId;
- private readonly Vector3? _crafterTurnInPosition;
-
- public LeveStepData(EAetheryteLocation aetheryteLocation,
- AethernetShortcut? aethernetShortcut,
- uint issuerDataId,
- Vector3 issuerPosition,
- uint? turnInDataId = null,
- Vector3? turnInPosition = null,
- uint? gathererTurnInDataId = null,
- Vector3? gathererTurnInPosition = null,
- uint? crafterTurnInDataId = null,
- Vector3? crafterTurnInPosition = null)
- {
- _turnInDataId = turnInDataId;
- _turnInPosition = turnInPosition;
- _gathererTurnInDataId = gathererTurnInDataId;
- _gathererTurnInPosition = gathererTurnInPosition;
- _crafterTurnInDataId = crafterTurnInDataId;
- _crafterTurnInPosition = crafterTurnInPosition;
- AetheryteLocation = aetheryteLocation;
- AethernetShortcut = aethernetShortcut;
- IssuerDataId = issuerDataId;
- IssuerPosition = issuerPosition;
- }
-
- public EAetheryteLocation AetheryteLocation { get; }
- public AethernetShortcut? AethernetShortcut { get; }
- public uint IssuerDataId { get; }
- public Vector3 IssuerPosition { get; }
-
- public uint GetTurnInDataId(LeveInfo leveInfo)
- {
- if (leveInfo.ClassJobs.Any(x => x.IsGatherer()))
- return _gathererTurnInDataId ?? _turnInDataId ?? IssuerDataId;
- else if (leveInfo.ClassJobs.Any(x => x.IsCrafter()))
- return _crafterTurnInDataId ?? _turnInDataId ?? IssuerDataId;
- else
- return _turnInDataId ?? IssuerDataId;
- }
-
- public Vector3 GetTurnInPosition(LeveInfo leveInfo)
- {
- if (leveInfo.ClassJobs.Any(x => x.IsGatherer()))
- return _gathererTurnInPosition ?? _turnInPosition ?? IssuerPosition;
- else if (leveInfo.ClassJobs.Any(x => x.IsCrafter()))
- return _crafterTurnInPosition ?? _turnInPosition ?? IssuerPosition;
- else
- return _turnInPosition ?? IssuerPosition;
- }
- }
-}
..dataManager.GetExcelSheet<SatisfactionNpc>()
.Where(x => x is { RowId: > 0, Npc.RowId: > 0 })
.Select(x => new SatisfactionSupplyInfo(x)),
- ..dataManager.GetExcelSheet<Leve>()
- .Where(x => x.RowId > 0)
- .Where(x => x.LevelLevemete.RowId != 0)
- .Select(x => new LeveInfo(x)),
];
quests.AddRange(
break;
case 2: // leve
- currentQuest = new LeveId(questManager->LeveQuests[trackedQuest.Index].LeveId);
- if (_questRegistry.IsKnownQuest(currentQuest))
- trackedQuests.Add((currentQuest,
- questManager->GetLeveQuestById(currentQuest.Value)->Sequence));
break;
}
}
QuestWork* questWork = QuestManager.Instance()->GetQuestById(questId.Value);
return questWork != null ? new QuestProgressInfo(*questWork) : null;
}
- else if (elementId is LeveId leveId)
- {
- LeveWork* leveWork = QuestManager.Instance()->GetLeveQuestById(leveId.Value);
- return leveWork != null ? new QuestProgressInfo(*leveWork) : null;
- }
else
return null;
}
{
if (elementId is QuestId questId)
return IsQuestAccepted(questId);
- else if (elementId is LeveId leveId)
- return IsQuestAccepted(leveId);
else if (elementId is SatisfactionSupplyNpcId)
return false;
else if (elementId is AlliedSocietyDailyId)
return questManager->IsQuestAccepted(questId.Value);
}
- public bool IsQuestAccepted(LeveId leveId)
- {
- QuestManager* questManager = QuestManager.Instance();
- foreach (var leveQuest in questManager->LeveQuests)
- {
- if (leveQuest.LeveId == leveId.Value)
- return true;
- }
-
- return false;
- }
-
public bool IsQuestComplete(ElementId elementId)
{
if (elementId is QuestId questId)
return IsQuestComplete(questId);
- else if (elementId is LeveId leveId)
- return IsQuestComplete(leveId);
else if (elementId is SatisfactionSupplyNpcId)
return false;
else if (elementId is AlliedSocietyDailyId)
return QuestManager.IsQuestComplete(questId.Value);
}
- public bool IsQuestComplete(LeveId leveId)
- {
- return QuestManager.Instance()->IsLevequestComplete(leveId.Value);
- }
-
public bool IsQuestLocked(ElementId elementId, ElementId? extraCompletedQuest = null)
{
if (elementId is QuestId questId)
return IsQuestLocked(questId, extraCompletedQuest);
- else if (elementId is LeveId leveId)
- return IsQuestLocked(leveId);
else if (elementId is SatisfactionSupplyNpcId satisfactionSupplyNpcId)
return IsQuestLocked(satisfactionSupplyNpcId);
else if (elementId is AlliedSocietyDailyId alliedSocietyDailyId)
return !HasCompletedPreviousQuests(questInfo, extraCompletedQuest) || !HasCompletedPreviousInstances(questInfo);
}
- private bool IsQuestLocked(LeveId leveId)
- {
- if (IsQuestUnobtainable(leveId))
- return true;
-
- // this only checks for the current class
- IQuestInfo questInfo = _questData.GetQuestInfo(leveId);
- if (!questInfo.ClassJobs.Contains((EClassJob)_clientState.LocalPlayer!.ClassJob.RowId) ||
- questInfo.Level > _clientState.LocalPlayer.Level)
- return true;
-
- return !IsQuestAccepted(leveId) && QuestManager.Instance()->NumLeveAllowances == 0;
- }
-
private bool IsQuestLocked(SatisfactionSupplyNpcId satisfactionSupplyNpcId)
{
SatisfactionSupplyInfo questInfo = (SatisfactionSupplyInfo)_questData.GetQuestInfo(satisfactionSupplyNpcId);
{
if (elementId is QuestId questId)
return IsQuestUnobtainable(questId, extraCompletedQuest);
- else if (elementId is LeveId leveId)
- return IsQuestUnobtainable(leveId);
else
return false;
}
return false;
}
- private bool IsQuestUnobtainable(LeveId leveId)
- {
- IQuestInfo questInfo = _questData.GetQuestInfo(leveId);
- if (questInfo.Expansion > (EExpansionVersion)PlayerState.Instance()->MaxExpansion)
- return true;
-
- return false;
- }
-
public bool IsQuestRemoved(ElementId elementId)
{
if (elementId is QuestId questId)
+++ /dev/null
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using LLib.GameData;
-using Lumina.Excel.Sheets;
-using Questionable.Model.Questing;
-
-namespace Questionable.Model;
-
-internal sealed class LeveInfo : IQuestInfo
-{
- public LeveInfo(Leve leve)
- {
- QuestId = new LeveId((ushort)leve.RowId);
- Name = leve.Name.ToString();
- Level = leve.ClassJobLevel;
- JournalGenre = leve.JournalGenre.RowId;
- SortKey = QuestId.Value;
- IssuerDataId = leve.LevelLevemete.Value.Object.RowId;
- ClassJobs = QuestInfoUtils.AsList(leve.ClassJobCategory.Value);
- Expansion = (EExpansionVersion)leve.LevelLevemete.Value.Territory.Value.ExVersion.RowId;
- }
-
- public ElementId QuestId { get; }
- public string Name { get; }
- public uint IssuerDataId { get; }
- public ImmutableList<PreviousQuestInfo> PreviousQuests { get; } = [];
- public EQuestJoin PreviousQuestJoin => EQuestJoin.All;
- public bool IsRepeatable => true;
- public ushort Level { get; }
- public EAlliedSociety AlliedSociety => EAlliedSociety.None;
- public uint? JournalGenre { get; }
- public ushort SortKey { get; }
- public bool IsMainScenarioQuest => false;
- public IReadOnlyList<EClassJob> ClassJobs { get; }
- public EExpansionVersion Expansion { get; }
-}
_asString = $"QW: {vars.Trim()}";
}
- public QuestProgressInfo(LeveWork leveWork)
- {
- Id = new LeveId(leveWork.LeveId);
- Sequence = leveWork.Sequence;
- Flags = leveWork.Flags;
- Variables = [0, 0, 0, 0, 0, 0];
- IsHidden = leveWork.IsHidden;
- ClassJob = (EClassJob)leveWork.ClearClass;
-
- _asString = $"Seed: {leveWork.LeveSeed}, Flags: {Flags:X}";
- }
-
public ElementId Id { get; }
public byte Sequence { get; }
public ushort Flags { get; init; }
using Questionable.Controller.Steps.Common;
using Questionable.Controller.Steps.Gathering;
using Questionable.Controller.Steps.Interactions;
-using Questionable.Controller.Steps.Leves;
using Questionable.Controller.Steps.Movement;
using Questionable.Controller.Utils;
using Questionable.Data;
serviceCollection.AddSingleton<AetheryteData>();
serviceCollection.AddSingleton<AlliedSocietyData>();
serviceCollection.AddSingleton<GatheringData>();
- serviceCollection.AddSingleton<LeveData>();
serviceCollection.AddSingleton<JournalData>();
serviceCollection.AddSingleton<QuestData>();
serviceCollection.AddSingleton<TerritoryData>();
.AddTaskFactoryAndExecutor<TurnInDelivery.Task, TurnInDelivery.Factory,
TurnInDelivery.SatisfactionSupplyTurnIn>();
- serviceCollection.AddTaskFactory<InitiateLeve.Factory>();
- serviceCollection
- .AddTaskExecutor<InitiateLeve.SkipInitiateIfActive, InitiateLeve.SkipInitiateIfActiveExecutor>();
- serviceCollection.AddTaskExecutor<InitiateLeve.OpenJournal, InitiateLeve.OpenJournalExecutor>();
- serviceCollection.AddTaskExecutor<InitiateLeve.Initiate, InitiateLeve.InitiateExecutor>();
- serviceCollection.AddTaskExecutor<InitiateLeve.SelectDifficulty, InitiateLeve.SelectDifficultyExecutor>();
-
serviceCollection.AddTaskFactory<SinglePlayerDuty.Factory>();
serviceCollection
.AddTaskExecutor<SinglePlayerDuty.StartSinglePlayerDuty, SinglePlayerDuty.StartSinglePlayerDutyExecutor>();
serviceCollection.AddSingleton<CreditsController>();
serviceCollection.AddSingleton<HelpUiController>();
serviceCollection.AddSingleton<InteractionUiController>();
- serviceCollection.AddSingleton<LeveUiController>();
serviceCollection.AddSingleton<ICombatModule, Mount128Module>();
serviceCollection.AddSingleton<ICombatModule, Mount147Module>();
serviceProvider.GetRequiredService<CraftworksSupplyController>();
serviceProvider.GetRequiredService<CreditsController>();
serviceProvider.GetRequiredService<HelpUiController>();
- serviceProvider.GetRequiredService<LeveUiController>();
serviceProvider.GetRequiredService<ShopController>();
serviceProvider.GetRequiredService<QuestionableIpc>();
serviceProvider.GetRequiredService<DalamudInitializer>();
yield break;
var questAccepts =
- FindQuestStepsWithInteractionType(quest, [EInteractionType.AcceptQuest, EInteractionType.AcceptLeve])
+ FindQuestStepsWithInteractionType(quest, [EInteractionType.AcceptQuest])
.Where(x => x.Step.PickUpQuestId == null)
.ToList();
foreach (var accept in questAccepts)
}
var questCompletes =
- FindQuestStepsWithInteractionType(quest, [EInteractionType.CompleteQuest, EInteractionType.CompleteLeve])
+ FindQuestStepsWithInteractionType(quest, [EInteractionType.CompleteQuest])
.Where(x => x.Step.TurnInQuestId == null)
.ToList();
foreach (var complete in questCompletes)
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using Dalamud.Interface;
.ToDictionary(x => x.Key, x => x.Select(y => y.GatheringPointId).ToList());
var gatheringLeveSheet = dataManager.GetExcelSheet<GatheringLeve>();
var territoryTypeSheet = dataManager.GetExcelSheet<TerritoryType>();
- var gatheringPointToLeve = dataManager.GetExcelSheet<Leve>()
+ var leveGatheringPoints = dataManager.GetExcelSheet<Leve>()
.Where(x => x.RowId > 0)
- .Select(x =>
- {
- uint startZonePlaceName = x.PlaceNameStartZone.RowId;
- startZonePlaceName = startZonePlaceName switch
- {
- 27 => 28, // limsa
- 39 => 52, // gridania
- 51 => 40, // uldah
- 62 => 2300, // ishgard
- _ => startZonePlaceName
- };
-
- var territoryType = territoryTypeSheet.Cast<TerritoryType?>()
- .FirstOrDefault(y => startZonePlaceName == y!.Value.PlaceName.RowId)
- ?? throw new InvalidOperationException($"Unable to use {startZonePlaceName}");
- return new
- {
- LeveId = x.RowId,
- LeveName = x.Name.ToString(),
- TerritoryType = (ushort)territoryType.RowId,
- TerritoryName = territoryType.PlaceName.ValueNullable?.Name.ToString(),
- Expansion = (EExpansionVersion)territoryType.ExVersion.RowId,
- GatheringLeve = gatheringLeveSheet.GetRowOrDefault(x.DataId.RowId),
- };
- })
- .Where(x => x.GatheringLeve != null)
- .Select(x => new
- {
- x.LeveId,
- x.LeveName,
- x.TerritoryType,
- x.TerritoryName,
- x.Expansion,
- GatheringPoints = x.GatheringLeve!.Value.Route
- .Where(y => y.RowId != 0)
- .SelectMany(y => routeToGatheringPoint[y.RowId]),
- })
- .SelectMany(x => x.GatheringPoints.Select(y => new
- {
- x.LeveId,
- x.LeveName,
- x.TerritoryType,
- x.TerritoryName,
- x.Expansion,
- GatheringPointId = y
- }))
- .GroupBy(x => x.GatheringPointId)
- .ToDictionary(x => x.Key, x => x.First());
+ .Select(x => gatheringLeveSheet.GetRowOrDefault(x.DataId.RowId))
+ .Where(x => x != null)
+ .Cast<GatheringLeve>()
+ .SelectMany(x => x.Route)
+ .Where(y => y.RowId != 0)
+ .SelectMany(y => routeToGatheringPoint[y.RowId])
+ .Distinct()
+ .ToHashSet();
var itemSheet = dataManager.GetExcelSheet<Item>();
.Where(x => x.Point.ClassJob != EClassJob.Fisher)
.Select(x =>
{
- if (gatheringPointToLeve.TryGetValue(x.GatheringPointId, out var leve))
- {
- // it's a leve
- return x.Point with
- {
- Expansion = leve.Expansion,
- TerritoryType = leve.TerritoryType,
- TerritoryName = leve.TerritoryName,
- PlaceName = $"Leve: {leve.LeveName}",
- };
- }
+ if (leveGatheringPoints.Contains(x.GatheringPointId))
+ return null;
else if (x.Point.TerritoryType == 1 &&
_gatheringPointRegistry.TryGetGatheringPoint(x.Point.Id, out GatheringRoot? gatheringRoot))
{
else
return x.Point;
})
+ .Where(x => x != null)
+ .Cast<DefaultGatheringPoint>()
.Where(x => x.Expansion != (EExpansionVersion)byte.MaxValue)
.Where(x => x.GatheringItemIds.Count > 0)
.Where(x => x.TerritoryType is not 901 and not 929) // exclude old diadem
ImGui.PopFont();
}
- if (currentQuest.Quest.Id is LeveId || currentQuest.Quest.Info.AlliedSociety != EAlliedSociety.None)
+ if (currentQuest.Quest.Info.AlliedSociety != EAlliedSociety.None)
{
ImGui.SameLine();
ImGui.Text($"/ {questWork.ClassJob}");
break;
case 2:
- ImGui.Text($"Leve: {questManager->LeveQuests[trackedQuest.Index].LeveId}, {trackedQuest.Index}");
break;
}
}
ImGui.SameLine();
if (knownQuest != null &&
- knownQuest.FindSequence(0)?.LastStep()?.InteractionType is EInteractionType.AcceptQuest
- or EInteractionType.AcceptLeve &&
+ knownQuest.FindSequence(0)?.LastStep()?.InteractionType is EInteractionType.AcceptQuest &&
_questFunctions.IsReadyToAcceptQuest(quest.QuestId))
{
ImGui.BeginDisabled(_questController.NextQuest != null || _questController.SimulatedQuest != null);