Migrate questIds to IId
This commit is contained in:
parent
ee2b49f566
commit
36a0f72bd9
@ -162,6 +162,7 @@ public class QuestSourceGenerator : ISourceGenerator
|
|||||||
SyntaxNodeList(
|
SyntaxNodeList(
|
||||||
AssignmentList(nameof(QuestRoot.Author), quest.Author)
|
AssignmentList(nameof(QuestRoot.Author), quest.Author)
|
||||||
.AsSyntaxNodeOrToken(),
|
.AsSyntaxNodeOrToken(),
|
||||||
|
Assignment(nameof(QuestRoot.Disabled), quest.Disabled, false).AsSyntaxNodeOrToken(),
|
||||||
Assignment(nameof(QuestRoot.Comment), quest.Comment, null)
|
Assignment(nameof(QuestRoot.Comment), quest.Comment, null)
|
||||||
.AsSyntaxNodeOrToken(),
|
.AsSyntaxNodeOrToken(),
|
||||||
AssignmentList(nameof(QuestRoot.TerritoryBlacklist),
|
AssignmentList(nameof(QuestRoot.TerritoryBlacklist),
|
||||||
@ -304,7 +305,8 @@ public class QuestSourceGenerator : ISourceGenerator
|
|||||||
Assignment(nameof(QuestStep.ContentFinderConditionId),
|
Assignment(nameof(QuestStep.ContentFinderConditionId),
|
||||||
step.ContentFinderConditionId, emptyStep.ContentFinderConditionId)
|
step.ContentFinderConditionId, emptyStep.ContentFinderConditionId)
|
||||||
.AsSyntaxNodeOrToken(),
|
.AsSyntaxNodeOrToken(),
|
||||||
Assignment(nameof(QuestStep.SkipConditions), step.SkipConditions, emptyStep.SkipConditions)
|
Assignment(nameof(QuestStep.SkipConditions), step.SkipConditions,
|
||||||
|
emptyStep.SkipConditions)
|
||||||
.AsSyntaxNodeOrToken(),
|
.AsSyntaxNodeOrToken(),
|
||||||
AssignmentList(nameof(QuestStep.RequiredQuestVariables),
|
AssignmentList(nameof(QuestStep.RequiredQuestVariables),
|
||||||
step.RequiredQuestVariables)
|
step.RequiredQuestVariables)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
@ -57,6 +56,24 @@ public static class RoslynShortcuts
|
|||||||
SyntaxKind.SimpleMemberAccessExpression,
|
SyntaxKind.SimpleMemberAccessExpression,
|
||||||
IdentifierName(value.GetType().Name),
|
IdentifierName(value.GetType().Name),
|
||||||
IdentifierName(value.GetType().GetEnumName(value)!));
|
IdentifierName(value.GetType().GetEnumName(value)!));
|
||||||
|
else if (value is QuestId questId)
|
||||||
|
{
|
||||||
|
return ObjectCreationExpression(
|
||||||
|
IdentifierName(nameof(QuestId)))
|
||||||
|
.WithArgumentList(
|
||||||
|
ArgumentList(
|
||||||
|
SingletonSeparatedList(
|
||||||
|
Argument(LiteralValue(questId.Value)))));
|
||||||
|
}
|
||||||
|
else if (value is LeveId leveId)
|
||||||
|
{
|
||||||
|
return ObjectCreationExpression(
|
||||||
|
IdentifierName(nameof(LeveId)))
|
||||||
|
.WithArgumentList(
|
||||||
|
ArgumentList(
|
||||||
|
SingletonSeparatedList(
|
||||||
|
Argument(LiteralValue(leveId.Value)))));
|
||||||
|
}
|
||||||
else if (value is Vector3 vector)
|
else if (value is Vector3 vector)
|
||||||
{
|
{
|
||||||
return ObjectCreationExpression(
|
return ObjectCreationExpression(
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
|
|
||||||
"Author": "liza",
|
|
||||||
"QuestSequence": [
|
|
||||||
{
|
|
||||||
"Sequence": 255,
|
|
||||||
"Steps": [
|
|
||||||
{
|
|
||||||
"Position": {
|
|
||||||
"X": -435.39066,
|
|
||||||
"Y": -9.809827,
|
|
||||||
"Z": -594.5472
|
|
||||||
},
|
|
||||||
"TerritoryId": 1187,
|
|
||||||
"InteractionType": "WalkTo",
|
|
||||||
"Fly": true,
|
|
||||||
"RequiredGatheredItems": [
|
|
||||||
{
|
|
||||||
"ItemId": 43992,
|
|
||||||
"ItemCount": 1234
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -39,8 +39,7 @@
|
|||||||
"ItemId": 35848,
|
"ItemId": 35848,
|
||||||
"ItemCount": 1
|
"ItemCount": 1
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"NextQuestId": 4159
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
19
Questionable.Model/Questing/Converter/IdConverter.cs
Normal file
19
Questionable.Model/Questing/Converter/IdConverter.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Questionable.Model.Questing.Converter;
|
||||||
|
|
||||||
|
public class IdConverter : JsonConverter<IId>
|
||||||
|
{
|
||||||
|
public override IId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
uint value = reader.GetUInt32();
|
||||||
|
return Id.From(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, IId value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
58
Questionable.Model/Questing/IId.cs
Normal file
58
Questionable.Model/Questing/IId.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Questionable.Model.Questing
|
||||||
|
{
|
||||||
|
public interface IId : IComparable<IId>
|
||||||
|
{
|
||||||
|
public ushort Value { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Id
|
||||||
|
{
|
||||||
|
public static IId From(uint value)
|
||||||
|
{
|
||||||
|
if (value >= 100_000 && value < 200_000)
|
||||||
|
return new LeveId((ushort)(value - 100_000));
|
||||||
|
else
|
||||||
|
return new QuestId((ushort)value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record QuestId(ushort Value) : IId
|
||||||
|
{
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "Q" + Value.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(IId? other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(this, other)) return 0;
|
||||||
|
if (ReferenceEquals(null, other)) return 1;
|
||||||
|
return Value.CompareTo(other.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record LeveId(ushort Value) : IId
|
||||||
|
{
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "L" + Value.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(IId? other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(this, other)) return 0;
|
||||||
|
if (ReferenceEquals(null, other)) return 1;
|
||||||
|
return Value.CompareTo(other.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace System.Runtime.CompilerServices
|
||||||
|
{
|
||||||
|
internal static class IsExternalInit
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -72,10 +72,14 @@ public sealed class QuestStep
|
|||||||
public IList<uint> PointMenuChoices { get; set; } = new List<uint>();
|
public IList<uint> PointMenuChoices { get; set; } = new List<uint>();
|
||||||
|
|
||||||
// TODO: Not implemented
|
// TODO: Not implemented
|
||||||
public ushort? PickUpQuestId { get; set; }
|
[JsonConverter(typeof(IdConverter))]
|
||||||
|
public IId? PickUpQuestId { get; set; }
|
||||||
|
|
||||||
public ushort? TurnInQuestId { get; set; }
|
[JsonConverter(typeof(IdConverter))]
|
||||||
public ushort? NextQuestId { get; set; }
|
public IId? TurnInQuestId { get; set; }
|
||||||
|
|
||||||
|
[JsonConverter(typeof(IdConverter))]
|
||||||
|
public IId? NextQuestId { get; set; }
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public QuestStep()
|
public QuestStep()
|
||||||
|
@ -13,8 +13,8 @@ public sealed class SkipStepConditions
|
|||||||
public List<ushort> InTerritory { get; set; } = new();
|
public List<ushort> InTerritory { get; set; } = new();
|
||||||
public List<ushort> NotInTerritory { get; set; } = new();
|
public List<ushort> NotInTerritory { get; set; } = new();
|
||||||
public SkipItemConditions? Item { get; set; }
|
public SkipItemConditions? Item { get; set; }
|
||||||
public List<ushort> QuestsAccepted { get; set; } = new();
|
public List<IId> QuestsAccepted { get; set; } = new();
|
||||||
public List<ushort> QuestsCompleted { get; set; } = new();
|
public List<IId> QuestsCompleted { get; set; } = new();
|
||||||
public EExtraSkipCondition? ExtraCondition { get; set; }
|
public EExtraSkipCondition? ExtraCondition { get; set; }
|
||||||
|
|
||||||
public bool HasSkipConditions()
|
public bool HasSkipConditions()
|
||||||
|
@ -168,9 +168,9 @@ internal sealed class CombatController : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (QuestWorkUtils.HasCompletionFlags(condition.CompletionQuestVariablesFlags))
|
if (QuestWorkUtils.HasCompletionFlags(condition.CompletionQuestVariablesFlags) && _currentFight.Data.QuestId is QuestId questId)
|
||||||
{
|
{
|
||||||
var questWork = _gameFunctions.GetQuestEx(_currentFight.Data.QuestId);
|
var questWork = _gameFunctions.GetQuestEx(questId);
|
||||||
if (questWork != null && QuestWorkUtils.MatchesQuestWork(condition.CompletionQuestVariablesFlags,
|
if (questWork != null && QuestWorkUtils.MatchesQuestWork(condition.CompletionQuestVariablesFlags,
|
||||||
questWork.Value))
|
questWork.Value))
|
||||||
{
|
{
|
||||||
@ -303,7 +303,7 @@ internal sealed class CombatController : IDisposable
|
|||||||
|
|
||||||
public sealed class CombatData
|
public sealed class CombatData
|
||||||
{
|
{
|
||||||
public required ushort QuestId { get; init; }
|
public required IId QuestId { get; init; }
|
||||||
public required EEnemySpawnType SpawnType { get; init; }
|
public required EEnemySpawnType SpawnType { get; init; }
|
||||||
public required List<uint> KillEnemyDataIds { get; init; }
|
public required List<uint> KillEnemyDataIds { get; init; }
|
||||||
public required List<ComplexCombatData> ComplexCombatDatas { get; init; }
|
public required List<ComplexCombatData> ComplexCombatDatas { get; init; }
|
||||||
|
@ -4,6 +4,7 @@ using Dalamud.Game.ClientState.Objects;
|
|||||||
using Dalamud.Game.Command;
|
using Dalamud.Game.Command;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Questionable.Model;
|
using Questionable.Model;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
using Questionable.Windows;
|
using Questionable.Windows;
|
||||||
using Questionable.Windows.QuestComponents;
|
using Questionable.Windows.QuestComponents;
|
||||||
|
|
||||||
@ -127,11 +128,11 @@ internal sealed class CommandHandler : IDisposable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arguments.Length >= 1 && ushort.TryParse(arguments[0], out ushort questId))
|
if (arguments.Length >= 1 && uint.TryParse(arguments[0], out uint questId))
|
||||||
{
|
{
|
||||||
if (_questRegistry.TryGetQuest(questId, out Quest? quest))
|
if (_questRegistry.TryGetQuest(Id.From(questId), out Quest? quest))
|
||||||
{
|
{
|
||||||
_debugOverlay.HighlightedQuest = questId;
|
_debugOverlay.HighlightedQuest = quest.QuestId;
|
||||||
_chatGui.Print($"[Questionable] Set highlighted quest to {questId} ({quest.Info.Name}).");
|
_chatGui.Print($"[Questionable] Set highlighted quest to {questId} ({quest.Info.Name}).");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -146,11 +147,11 @@ internal sealed class CommandHandler : IDisposable
|
|||||||
|
|
||||||
private void SetNextQuest(string[] arguments)
|
private void SetNextQuest(string[] arguments)
|
||||||
{
|
{
|
||||||
if (arguments.Length >= 1 && ushort.TryParse(arguments[0], out ushort questId))
|
if (arguments.Length >= 1 && uint.TryParse(arguments[0], out uint questId))
|
||||||
{
|
{
|
||||||
if (_gameFunctions.IsQuestLocked(questId, 0))
|
if (_gameFunctions.IsQuestLocked(Id.From(questId)))
|
||||||
_chatGui.PrintError($"[Questionable] Quest {questId} is locked.");
|
_chatGui.PrintError($"[Questionable] Quest {questId} is locked.");
|
||||||
else if (_questRegistry.TryGetQuest(questId, out Quest? quest))
|
else if (_questRegistry.TryGetQuest(Id.From(questId), out Quest? quest))
|
||||||
{
|
{
|
||||||
_questController.SetNextQuest(quest);
|
_questController.SetNextQuest(quest);
|
||||||
_chatGui.Print($"[Questionable] Set next quest to {questId} ({quest.Info.Name}).");
|
_chatGui.Print($"[Questionable] Set next quest to {questId} ({quest.Info.Name}).");
|
||||||
@ -171,7 +172,7 @@ internal sealed class CommandHandler : IDisposable
|
|||||||
{
|
{
|
||||||
if (arguments.Length >= 1 && ushort.TryParse(arguments[0], out ushort questId))
|
if (arguments.Length >= 1 && ushort.TryParse(arguments[0], out ushort questId))
|
||||||
{
|
{
|
||||||
if (_questRegistry.TryGetQuest(questId, out Quest? quest))
|
if (_questRegistry.TryGetQuest(Id.From(questId), out Quest? quest))
|
||||||
{
|
{
|
||||||
_questController.SimulateQuest(quest);
|
_questController.SimulateQuest(quest);
|
||||||
_chatGui.Print($"[Questionable] Simulating quest {questId} ({quest.Info.Name}).");
|
_chatGui.Print($"[Questionable] Simulating quest {questId} ({quest.Info.Name}).");
|
||||||
|
@ -600,7 +600,7 @@ internal sealed class GameUiController : IDisposable
|
|||||||
|
|
||||||
private unsafe void UnendingCodexPostSetup(AddonEvent type, AddonArgs args)
|
private unsafe void UnendingCodexPostSetup(AddonEvent type, AddonArgs args)
|
||||||
{
|
{
|
||||||
if (_questController.StartedQuest?.Quest.QuestId == 4526)
|
if (_questController.StartedQuest?.Quest.QuestId.Value == 4526)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Closing Unending Codex");
|
_logger.LogInformation("Closing Unending Codex");
|
||||||
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
||||||
@ -610,7 +610,7 @@ internal sealed class GameUiController : IDisposable
|
|||||||
|
|
||||||
private unsafe void ContentsTutorialPostSetup(AddonEvent type, AddonArgs args)
|
private unsafe void ContentsTutorialPostSetup(AddonEvent type, AddonArgs args)
|
||||||
{
|
{
|
||||||
if (_questController.StartedQuest?.Quest.QuestId == 245)
|
if (_questController.StartedQuest?.Quest.QuestId.Value == 245)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Closing ContentsTutorial");
|
_logger.LogInformation("Closing ContentsTutorial");
|
||||||
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
||||||
@ -623,7 +623,7 @@ internal sealed class GameUiController : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private unsafe void MultipleHelpWindowPostSetup(AddonEvent type, AddonArgs args)
|
private unsafe void MultipleHelpWindowPostSetup(AddonEvent type, AddonArgs args)
|
||||||
{
|
{
|
||||||
if (_questController.StartedQuest?.Quest.QuestId == 245)
|
if (_questController.StartedQuest?.Quest.QuestId.Value == 245)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Closing MultipleHelpWindow");
|
_logger.LogInformation("Closing MultipleHelpWindow");
|
||||||
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
||||||
|
@ -209,8 +209,8 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(ushort currentQuestId, currentSequence) = _gameFunctions.GetCurrentQuest();
|
(IId? currentQuestId, currentSequence) = _gameFunctions.GetCurrentQuest();
|
||||||
if (currentQuestId == 0)
|
if (currentQuestId == null || currentQuestId.Value == 0)
|
||||||
{
|
{
|
||||||
if (_startedQuest != null)
|
if (_startedQuest != null)
|
||||||
{
|
{
|
||||||
@ -330,7 +330,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
return (seq, seq.Steps[CurrentQuest.Step]);
|
return (seq, seq.Steps[CurrentQuest.Step]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void IncreaseStepCount(ushort? questId, int? sequence, bool shouldContinue = false)
|
public void IncreaseStepCount(IId? questId, int? sequence, bool shouldContinue = false)
|
||||||
{
|
{
|
||||||
lock (_progressLock)
|
lock (_progressLock)
|
||||||
{
|
{
|
||||||
@ -545,7 +545,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Skip(ushort questQuestId, byte currentQuestSequence)
|
public void Skip(IId questQuestId, byte currentQuestSequence)
|
||||||
{
|
{
|
||||||
lock (_progressLock)
|
lock (_progressLock)
|
||||||
{
|
{
|
||||||
@ -609,8 +609,9 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
|||||||
1158, // Titan (Hard)
|
1158, // Titan (Hard)
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach (var questId in priorityQuests)
|
foreach (var id in priorityQuests)
|
||||||
{
|
{
|
||||||
|
var questId = new QuestId(id);
|
||||||
if (_gameFunctions.IsReadyToAcceptQuest(questId) && _questRegistry.TryGetQuest(questId, out var quest))
|
if (_gameFunctions.IsReadyToAcceptQuest(questId) && _questRegistry.TryGetQuest(questId, out var quest))
|
||||||
{
|
{
|
||||||
SetNextQuest(quest);
|
SetNextQuest(quest);
|
||||||
|
@ -28,7 +28,7 @@ internal sealed class QuestRegistry
|
|||||||
private readonly ILogger<QuestRegistry> _logger;
|
private readonly ILogger<QuestRegistry> _logger;
|
||||||
private readonly ICallGateProvider<object> _reloadDataIpc;
|
private readonly ICallGateProvider<object> _reloadDataIpc;
|
||||||
|
|
||||||
private readonly Dictionary<ushort, Quest> _quests = new();
|
private readonly Dictionary<IId, Quest> _quests = new();
|
||||||
|
|
||||||
public QuestRegistry(IDalamudPluginInterface pluginInterface, QuestData questData,
|
public QuestRegistry(IDalamudPluginInterface pluginInterface, QuestData questData,
|
||||||
QuestValidator questValidator, JsonSchemaValidator jsonSchemaValidator,
|
QuestValidator questValidator, JsonSchemaValidator jsonSchemaValidator,
|
||||||
@ -91,12 +91,12 @@ internal sealed class QuestRegistry
|
|||||||
{
|
{
|
||||||
Quest quest = new()
|
Quest quest = new()
|
||||||
{
|
{
|
||||||
QuestId = questId,
|
QuestId = new QuestId(questId),
|
||||||
Root = questRoot,
|
Root = questRoot,
|
||||||
Info = _questData.GetQuestInfo(questId),
|
Info = _questData.GetQuestInfo(new QuestId(questId)),
|
||||||
ReadOnly = true,
|
ReadOnly = true,
|
||||||
};
|
};
|
||||||
_quests[questId] = quest;
|
_quests[quest.QuestId] = quest;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Loaded {Count} quests from assembly", _quests.Count);
|
_logger.LogInformation("Loaded {Count} quests from assembly", _quests.Count);
|
||||||
@ -136,21 +136,21 @@ internal sealed class QuestRegistry
|
|||||||
private void LoadQuestFromStream(string fileName, Stream stream)
|
private void LoadQuestFromStream(string fileName, Stream stream)
|
||||||
{
|
{
|
||||||
_logger.LogTrace("Loading quest from '{FileName}'", fileName);
|
_logger.LogTrace("Loading quest from '{FileName}'", fileName);
|
||||||
ushort? questId = ExtractQuestIdFromName(fileName);
|
IId? questId = ExtractQuestIdFromName(fileName);
|
||||||
if (questId == null)
|
if (questId == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var questNode = JsonNode.Parse(stream)!;
|
var questNode = JsonNode.Parse(stream)!;
|
||||||
_jsonSchemaValidator.Enqueue(questId.Value, questNode);
|
_jsonSchemaValidator.Enqueue(questId, questNode);
|
||||||
|
|
||||||
Quest quest = new Quest
|
Quest quest = new Quest
|
||||||
{
|
{
|
||||||
QuestId = questId.Value,
|
QuestId = questId,
|
||||||
Root = questNode.Deserialize<QuestRoot>()!,
|
Root = questNode.Deserialize<QuestRoot>()!,
|
||||||
Info = _questData.GetQuestInfo(questId.Value),
|
Info = _questData.GetQuestInfo(questId),
|
||||||
ReadOnly = false,
|
ReadOnly = false,
|
||||||
};
|
};
|
||||||
_quests[questId.Value] = quest;
|
_quests[quest.QuestId] = quest;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadFromDirectory(DirectoryInfo directory, LogLevel logLevel = LogLevel.Information)
|
private void LoadFromDirectory(DirectoryInfo directory, LogLevel logLevel = LogLevel.Information)
|
||||||
@ -179,7 +179,7 @@ internal sealed class QuestRegistry
|
|||||||
LoadFromDirectory(childDirectory, logLevel);
|
LoadFromDirectory(childDirectory, logLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ushort? ExtractQuestIdFromName(string resourceName)
|
private static IId? ExtractQuestIdFromName(string resourceName)
|
||||||
{
|
{
|
||||||
string name = resourceName.Substring(0, resourceName.Length - ".json".Length);
|
string name = resourceName.Substring(0, resourceName.Length - ".json".Length);
|
||||||
name = name.Substring(name.LastIndexOf('.') + 1);
|
name = name.Substring(name.LastIndexOf('.') + 1);
|
||||||
@ -188,11 +188,30 @@ internal sealed class QuestRegistry
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
string[] parts = name.Split('_', 2);
|
string[] parts = name.Split('_', 2);
|
||||||
return ushort.Parse(parts[0], CultureInfo.InvariantCulture);
|
return Id.From(uint.Parse(parts[0], CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsKnownQuest(ushort questId) => _quests.ContainsKey(questId);
|
public bool IsKnownQuest(IId id)
|
||||||
|
{
|
||||||
|
if (id is QuestId questId)
|
||||||
|
return IsKnownQuest(questId);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryGetQuest(ushort questId, [NotNullWhen(true)] out Quest? quest)
|
public bool IsKnownQuest(QuestId questId) => _quests.ContainsKey(questId);
|
||||||
|
|
||||||
|
public bool TryGetQuest(IId id, [NotNullWhen(true)] out Quest? quest)
|
||||||
|
{
|
||||||
|
if (id is QuestId questId)
|
||||||
|
return TryGetQuest(questId, out quest);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
quest = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetQuest(QuestId questId, [NotNullWhen(true)] out Quest? quest)
|
||||||
=> _quests.TryGetValue(questId, out quest);
|
=> _quests.TryGetValue(questId, out quest);
|
||||||
}
|
}
|
||||||
|
@ -18,20 +18,20 @@ internal static class NextQuest
|
|||||||
if (step.NextQuestId == null)
|
if (step.NextQuestId == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (step.NextQuestId.Value == quest.QuestId)
|
if (step.NextQuestId == quest.QuestId)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return serviceProvider.GetRequiredService<SetQuest>()
|
return serviceProvider.GetRequiredService<SetQuest>()
|
||||||
.With(step.NextQuestId.Value, quest.QuestId);
|
.With(step.NextQuestId, quest.QuestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class SetQuest(QuestRegistry questRegistry, QuestController questController, GameFunctions gameFunctions, ILogger<SetQuest> logger) : ITask
|
internal sealed class SetQuest(QuestRegistry questRegistry, QuestController questController, GameFunctions gameFunctions, ILogger<SetQuest> logger) : ITask
|
||||||
{
|
{
|
||||||
public ushort NextQuestId { get; set; }
|
public IId NextQuestId { get; set; } = null!;
|
||||||
public ushort CurrentQuestId { get; set; }
|
public IId CurrentQuestId { get; set; } = null!;
|
||||||
|
|
||||||
public ITask With(ushort nextQuestId, ushort currentQuestId)
|
public ITask With(IId nextQuestId, IId currentQuestId)
|
||||||
{
|
{
|
||||||
NextQuestId = nextQuestId;
|
NextQuestId = nextQuestId;
|
||||||
CurrentQuestId = currentQuestId;
|
CurrentQuestId = currentQuestId;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
namespace Questionable.Controller.Steps;
|
using Questionable.Model.Questing;
|
||||||
|
|
||||||
|
namespace Questionable.Controller.Steps;
|
||||||
|
|
||||||
internal interface ILastTask : ITask
|
internal interface ILastTask : ITask
|
||||||
{
|
{
|
||||||
public ushort QuestId { get; }
|
public IId QuestId { get; }
|
||||||
public int Sequence { get; }
|
public int Sequence { get; }
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ internal static class Combat
|
|||||||
private CombatController.CombatData _combatData = null!;
|
private CombatController.CombatData _combatData = null!;
|
||||||
private IList<QuestWorkValue?> _completionQuestVariableFlags = null!;
|
private IList<QuestWorkValue?> _completionQuestVariableFlags = null!;
|
||||||
|
|
||||||
public ITask With(ushort questId, bool isLastStep, EEnemySpawnType enemySpawnType, IList<uint> killEnemyDataIds,
|
public ITask With(IId questId, bool isLastStep, EEnemySpawnType enemySpawnType, IList<uint> killEnemyDataIds,
|
||||||
IList<QuestWorkValue?> completionQuestVariablesFlags, IList<ComplexCombatData> complexCombatData)
|
IList<QuestWorkValue?> completionQuestVariablesFlags, IList<ComplexCombatData> complexCombatData)
|
||||||
{
|
{
|
||||||
_isLastStep = isLastStep;
|
_isLastStep = isLastStep;
|
||||||
@ -107,9 +107,9 @@ internal static class Combat
|
|||||||
return ETaskResult.StillRunning;
|
return ETaskResult.StillRunning;
|
||||||
|
|
||||||
// if our quest step has any completion flags, we need to check if they are set
|
// if our quest step has any completion flags, we need to check if they are set
|
||||||
if (QuestWorkUtils.HasCompletionFlags(_completionQuestVariableFlags))
|
if (QuestWorkUtils.HasCompletionFlags(_completionQuestVariableFlags) && _combatData.QuestId is QuestId questId)
|
||||||
{
|
{
|
||||||
var questWork = gameFunctions.GetQuestEx(_combatData.QuestId);
|
var questWork = gameFunctions.GetQuestEx(questId);
|
||||||
if (questWork == null)
|
if (questWork == null)
|
||||||
return ETaskResult.StillRunning;
|
return ETaskResult.StillRunning;
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ internal static class UseItem
|
|||||||
private DateTime _continueAt;
|
private DateTime _continueAt;
|
||||||
private int _itemCount;
|
private int _itemCount;
|
||||||
|
|
||||||
public ushort? QuestId { get; set; }
|
public IId? QuestId { get; set; }
|
||||||
public uint ItemId { get; set; }
|
public uint ItemId { get; set; }
|
||||||
public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue?>();
|
public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue?>();
|
||||||
public bool StartingCombat { get; set; }
|
public bool StartingCombat { get; set; }
|
||||||
@ -142,9 +142,9 @@ internal static class UseItem
|
|||||||
|
|
||||||
public unsafe ETaskResult Update()
|
public unsafe ETaskResult Update()
|
||||||
{
|
{
|
||||||
if (QuestId.HasValue && QuestWorkUtils.HasCompletionFlags(CompletionQuestVariablesFlags))
|
if (QuestId is QuestId questId && QuestWorkUtils.HasCompletionFlags(CompletionQuestVariablesFlags))
|
||||||
{
|
{
|
||||||
QuestWork? questWork = gameFunctions.GetQuestEx(QuestId.Value);
|
QuestWork? questWork = gameFunctions.GetQuestEx(questId);
|
||||||
if (questWork != null &&
|
if (questWork != null &&
|
||||||
QuestWorkUtils.MatchesQuestWork(CompletionQuestVariablesFlags, questWork.Value))
|
QuestWorkUtils.MatchesQuestWork(CompletionQuestVariablesFlags, questWork.Value))
|
||||||
return ETaskResult.TaskComplete;
|
return ETaskResult.TaskComplete;
|
||||||
@ -203,7 +203,7 @@ internal static class UseItem
|
|||||||
|
|
||||||
public uint DataId { get; set; }
|
public uint DataId { get; set; }
|
||||||
|
|
||||||
public ITask With(ushort? questId, uint dataId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
|
public ITask With(IId? questId, uint dataId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
|
||||||
{
|
{
|
||||||
QuestId = questId;
|
QuestId = questId;
|
||||||
DataId = dataId;
|
DataId = dataId;
|
||||||
@ -227,7 +227,7 @@ internal static class UseItem
|
|||||||
|
|
||||||
public Vector3 Position { get; set; }
|
public Vector3 Position { get; set; }
|
||||||
|
|
||||||
public ITask With(ushort? questId, Vector3 position, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
|
public ITask With(IId? questId, Vector3 position, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
|
||||||
{
|
{
|
||||||
QuestId = questId;
|
QuestId = questId;
|
||||||
Position = position;
|
Position = position;
|
||||||
@ -249,7 +249,7 @@ internal static class UseItem
|
|||||||
|
|
||||||
public uint DataId { get; set; }
|
public uint DataId { get; set; }
|
||||||
|
|
||||||
public ITask With(ushort? questId, uint dataId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags,
|
public ITask With(IId? questId, uint dataId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags,
|
||||||
bool startingCombat = false)
|
bool startingCombat = false)
|
||||||
{
|
{
|
||||||
QuestId = questId;
|
QuestId = questId;
|
||||||
@ -270,7 +270,7 @@ internal static class UseItem
|
|||||||
{
|
{
|
||||||
private readonly GameFunctions _gameFunctions = gameFunctions;
|
private readonly GameFunctions _gameFunctions = gameFunctions;
|
||||||
|
|
||||||
public ITask With(ushort? questId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
|
public ITask With(IId? questId, uint itemId, IList<QuestWorkValue?> completionQuestVariablesFlags)
|
||||||
{
|
{
|
||||||
QuestId = questId;
|
QuestId = questId;
|
||||||
ItemId = itemId;
|
ItemId = itemId;
|
||||||
|
@ -45,9 +45,9 @@ internal static class SkipCondition
|
|||||||
{
|
{
|
||||||
public QuestStep Step { get; set; } = null!;
|
public QuestStep Step { get; set; } = null!;
|
||||||
public SkipStepConditions SkipConditions { get; set; } = null!;
|
public SkipStepConditions SkipConditions { get; set; } = null!;
|
||||||
public ushort QuestId { get; set; }
|
public IId QuestId { get; set; } = null!;
|
||||||
|
|
||||||
public ITask With(QuestStep step, SkipStepConditions skipConditions, ushort questId)
|
public ITask With(QuestStep step, SkipStepConditions skipConditions, IId questId)
|
||||||
{
|
{
|
||||||
Step = step;
|
Step = step;
|
||||||
SkipConditions = skipConditions;
|
SkipConditions = skipConditions;
|
||||||
@ -156,32 +156,35 @@ internal static class SkipCondition
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QuestWork? questWork = gameFunctions.GetQuestEx(QuestId);
|
if (QuestId is QuestId questId)
|
||||||
if (QuestWorkUtils.HasCompletionFlags(Step.CompletionQuestVariablesFlags) && questWork != null)
|
|
||||||
{
|
{
|
||||||
if (QuestWorkUtils.MatchesQuestWork(Step.CompletionQuestVariablesFlags, questWork.Value))
|
QuestWork? questWork = gameFunctions.GetQuestEx(questId);
|
||||||
|
if (QuestWorkUtils.HasCompletionFlags(Step.CompletionQuestVariablesFlags) && questWork != null)
|
||||||
{
|
{
|
||||||
logger.LogInformation("Skipping step, as quest variables match (step is complete)");
|
if (QuestWorkUtils.MatchesQuestWork(Step.CompletionQuestVariablesFlags, questWork.Value))
|
||||||
return true;
|
{
|
||||||
|
logger.LogInformation("Skipping step, as quest variables match (step is complete)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (Step is { SkipConditions.StepIf: { } conditions } && questWork != null)
|
if (Step is { SkipConditions.StepIf: { } conditions } && questWork != null)
|
||||||
{
|
|
||||||
if (QuestWorkUtils.MatchesQuestWork(conditions.CompletionQuestVariablesFlags, questWork.Value))
|
|
||||||
{
|
{
|
||||||
logger.LogInformation("Skipping step, as quest variables match (step can be skipped)");
|
if (QuestWorkUtils.MatchesQuestWork(conditions.CompletionQuestVariablesFlags, questWork.Value))
|
||||||
return true;
|
{
|
||||||
|
logger.LogInformation("Skipping step, as quest variables match (step can be skipped)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (Step is { RequiredQuestVariables: { } requiredQuestVariables } && questWork != null)
|
if (Step is { RequiredQuestVariables: { } requiredQuestVariables } && questWork != null)
|
||||||
{
|
|
||||||
if (!QuestWorkUtils.MatchesRequiredQuestWorkConfig(requiredQuestVariables, questWork.Value,
|
|
||||||
logger))
|
|
||||||
{
|
{
|
||||||
logger.LogInformation("Skipping step, as required variables do not match");
|
if (!QuestWorkUtils.MatchesRequiredQuestWorkConfig(requiredQuestVariables, questWork.Value,
|
||||||
return true;
|
logger))
|
||||||
|
{
|
||||||
|
logger.LogInformation("Skipping step, as required variables do not match");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,13 +198,13 @@ internal static class SkipCondition
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Step.PickUpQuestId != null && gameFunctions.IsQuestAcceptedOrComplete(Step.PickUpQuestId.Value))
|
if (Step.PickUpQuestId != null && gameFunctions.IsQuestAcceptedOrComplete(Step.PickUpQuestId))
|
||||||
{
|
{
|
||||||
logger.LogInformation("Skipping step, as we have already picked up the relevant quest");
|
logger.LogInformation("Skipping step, as we have already picked up the relevant quest");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Step.TurnInQuestId != null && gameFunctions.IsQuestComplete(Step.TurnInQuestId.Value))
|
if (Step.TurnInQuestId != null && gameFunctions.IsQuestComplete(Step.TurnInQuestId))
|
||||||
{
|
{
|
||||||
logger.LogInformation("Skipping step, as we have already completed the relevant quest");
|
logger.LogInformation("Skipping step, as we have already completed the relevant quest");
|
||||||
return true;
|
return true;
|
||||||
|
@ -30,7 +30,7 @@ internal static class WaitAtEnd
|
|||||||
if (step.CompletionQuestVariablesFlags.Count == 6 && QuestWorkUtils.HasCompletionFlags(step.CompletionQuestVariablesFlags))
|
if (step.CompletionQuestVariablesFlags.Count == 6 && QuestWorkUtils.HasCompletionFlags(step.CompletionQuestVariablesFlags))
|
||||||
{
|
{
|
||||||
var task = serviceProvider.GetRequiredService<WaitForCompletionFlags>()
|
var task = serviceProvider.GetRequiredService<WaitForCompletionFlags>()
|
||||||
.With(quest, step);
|
.With((QuestId)quest.QuestId, step);
|
||||||
var delay = serviceProvider.GetRequiredService<WaitDelay>();
|
var delay = serviceProvider.GetRequiredService<WaitDelay>();
|
||||||
return [task, delay, Next(quest, sequence)];
|
return [task, delay, Next(quest, sequence)];
|
||||||
}
|
}
|
||||||
@ -162,11 +162,11 @@ internal static class WaitAtEnd
|
|||||||
|
|
||||||
internal sealed class WaitForCompletionFlags(GameFunctions gameFunctions) : ITask
|
internal sealed class WaitForCompletionFlags(GameFunctions gameFunctions) : ITask
|
||||||
{
|
{
|
||||||
public Quest Quest { get; set; } = null!;
|
public QuestId Quest { get; set; } = null!;
|
||||||
public QuestStep Step { get; set; } = null!;
|
public QuestStep Step { get; set; } = null!;
|
||||||
public IList<QuestWorkValue?> Flags { get; set; } = null!;
|
public IList<QuestWorkValue?> Flags { get; set; } = null!;
|
||||||
|
|
||||||
public ITask With(Quest quest, QuestStep step)
|
public ITask With(QuestId quest, QuestStep step)
|
||||||
{
|
{
|
||||||
Quest = quest;
|
Quest = quest;
|
||||||
Step = step;
|
Step = step;
|
||||||
@ -178,7 +178,7 @@ internal static class WaitAtEnd
|
|||||||
|
|
||||||
public ETaskResult Update()
|
public ETaskResult Update()
|
||||||
{
|
{
|
||||||
QuestWork? questWork = gameFunctions.GetQuestEx(Quest.QuestId);
|
QuestWork? questWork = gameFunctions.GetQuestEx(Quest);
|
||||||
return questWork != null &&
|
return questWork != null &&
|
||||||
QuestWorkUtils.MatchesQuestWork(Step.CompletionQuestVariablesFlags, questWork.Value)
|
QuestWorkUtils.MatchesQuestWork(Step.CompletionQuestVariablesFlags, questWork.Value)
|
||||||
? ETaskResult.TaskComplete
|
? ETaskResult.TaskComplete
|
||||||
@ -214,11 +214,11 @@ internal static class WaitAtEnd
|
|||||||
$"WaitObj({DataId} at {Destination.ToString("G", CultureInfo.InvariantCulture)} < {Distance})";
|
$"WaitObj({DataId} at {Destination.ToString("G", CultureInfo.InvariantCulture)} < {Distance})";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class WaitQuestAccepted : ITask
|
internal sealed class WaitQuestAccepted(GameFunctions gameFunctions) : ITask
|
||||||
{
|
{
|
||||||
public ushort QuestId { get; set; }
|
public IId QuestId { get; set; } = null!;
|
||||||
|
|
||||||
public ITask With(ushort questId)
|
public ITask With(IId questId)
|
||||||
{
|
{
|
||||||
QuestId = questId;
|
QuestId = questId;
|
||||||
return this;
|
return this;
|
||||||
@ -228,23 +228,19 @@ internal static class WaitAtEnd
|
|||||||
|
|
||||||
public ETaskResult Update()
|
public ETaskResult Update()
|
||||||
{
|
{
|
||||||
unsafe
|
return gameFunctions.IsQuestAccepted(QuestId)
|
||||||
{
|
? ETaskResult.TaskComplete
|
||||||
var questManager = QuestManager.Instance();
|
: ETaskResult.StillRunning;
|
||||||
return questManager != null && questManager->IsQuestAccepted(QuestId)
|
|
||||||
? ETaskResult.TaskComplete
|
|
||||||
: ETaskResult.StillRunning;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => $"WaitQuestAccepted({QuestId})";
|
public override string ToString() => $"WaitQuestAccepted({QuestId})";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class WaitQuestCompleted : ITask
|
internal sealed class WaitQuestCompleted(GameFunctions gameFunctions) : ITask
|
||||||
{
|
{
|
||||||
public ushort QuestId { get; set; }
|
public IId QuestId { get; set; } = null!;
|
||||||
|
|
||||||
public ITask With(ushort questId)
|
public ITask With(IId questId)
|
||||||
{
|
{
|
||||||
QuestId = questId;
|
QuestId = questId;
|
||||||
return this;
|
return this;
|
||||||
@ -254,15 +250,15 @@ internal static class WaitAtEnd
|
|||||||
|
|
||||||
public ETaskResult Update()
|
public ETaskResult Update()
|
||||||
{
|
{
|
||||||
return QuestManager.IsQuestComplete(QuestId) ? ETaskResult.TaskComplete : ETaskResult.StillRunning;
|
return gameFunctions.IsQuestComplete(QuestId) ? ETaskResult.TaskComplete : ETaskResult.StillRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => $"WaitQuestComplete({QuestId})";
|
public override string ToString() => $"WaitQuestComplete({QuestId})";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class NextStep(ushort questId, int sequence) : ILastTask
|
internal sealed class NextStep(IId questId, int sequence) : ILastTask
|
||||||
{
|
{
|
||||||
public ushort QuestId { get; } = questId;
|
public IId QuestId { get; } = questId;
|
||||||
public int Sequence { get; } = sequence;
|
public int Sequence { get; } = sequence;
|
||||||
|
|
||||||
public bool Start() => true;
|
public bool Start() => true;
|
||||||
@ -274,7 +270,7 @@ internal static class WaitAtEnd
|
|||||||
|
|
||||||
internal sealed class EndAutomation : ILastTask
|
internal sealed class EndAutomation : ILastTask
|
||||||
{
|
{
|
||||||
public ushort QuestId => throw new InvalidOperationException();
|
public IId QuestId => throw new InvalidOperationException();
|
||||||
public int Sequence => throw new InvalidOperationException();
|
public int Sequence => throw new InvalidOperationException();
|
||||||
|
|
||||||
public bool Start() => true;
|
public bool Start() => true;
|
||||||
|
@ -3,6 +3,7 @@ using System.Linq;
|
|||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using Questionable.Model;
|
using Questionable.Model;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
|
|
||||||
namespace Questionable.Data;
|
namespace Questionable.Data;
|
||||||
|
|
||||||
@ -21,15 +22,17 @@ internal sealed class JournalData
|
|||||||
var genreLimsa = new Genre(uint.MaxValue - 3, "Starting in Limsa Lominsa", 1,
|
var genreLimsa = new Genre(uint.MaxValue - 3, "Starting in Limsa Lominsa", 1,
|
||||||
new uint[] { 108, 109 }.Concat(limsaStart.Quest.Select(x => x.Row))
|
new uint[] { 108, 109 }.Concat(limsaStart.Quest.Select(x => x.Row))
|
||||||
.Where(x => x != 0)
|
.Where(x => x != 0)
|
||||||
.Select(x => questData.GetQuestInfo((ushort)(x & 0xFFFF))).ToList());
|
.Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
|
||||||
|
.ToList());
|
||||||
var genreGridania = new Genre(uint.MaxValue - 2, "Starting in Gridania", 1,
|
var genreGridania = new Genre(uint.MaxValue - 2, "Starting in Gridania", 1,
|
||||||
new uint[] { 85, 123, 124 }.Concat(gridaniaStart.Quest.Select(x => x.Row))
|
new uint[] { 85, 123, 124 }.Concat(gridaniaStart.Quest.Select(x => x.Row))
|
||||||
.Where(x => x != 0)
|
.Where(x => x != 0)
|
||||||
.Select(x => questData.GetQuestInfo((ushort)(x & 0xFFFF))).ToList());
|
.Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
|
||||||
|
.ToList());
|
||||||
var genreUldah = new Genre(uint.MaxValue - 1, "Starting in Ul'dah", 1,
|
var genreUldah = new Genre(uint.MaxValue - 1, "Starting in Ul'dah", 1,
|
||||||
new uint[] { 568, 569, 570 }.Concat(uldahStart.Quest.Select(x => x.Row))
|
new uint[] { 568, 569, 570 }.Concat(uldahStart.Quest.Select(x => x.Row))
|
||||||
.Where(x => x != 0)
|
.Where(x => x != 0)
|
||||||
.Select(x => questData.GetQuestInfo((ushort)(x & 0xFFFF)))
|
.Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
|
||||||
.ToList());
|
.ToList());
|
||||||
genres.InsertRange(0, [genreLimsa, genreGridania, genreUldah]);
|
genres.InsertRange(0, [genreLimsa, genreGridania, genreUldah]);
|
||||||
genres.Single(x => x.Id == 1)
|
genres.Single(x => x.Id == 1)
|
||||||
|
@ -4,13 +4,14 @@ using System.Collections.Immutable;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Questionable.Model;
|
using Questionable.Model;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
using Quest = Lumina.Excel.GeneratedSheets.Quest;
|
using Quest = Lumina.Excel.GeneratedSheets.Quest;
|
||||||
|
|
||||||
namespace Questionable.Data;
|
namespace Questionable.Data;
|
||||||
|
|
||||||
internal sealed class QuestData
|
internal sealed class QuestData
|
||||||
{
|
{
|
||||||
private readonly ImmutableDictionary<ushort, QuestInfo> _quests;
|
private readonly ImmutableDictionary<QuestId, QuestInfo> _quests;
|
||||||
|
|
||||||
public QuestData(IDataManager dataManager)
|
public QuestData(IDataManager dataManager)
|
||||||
{
|
{
|
||||||
@ -22,7 +23,15 @@ internal sealed class QuestData
|
|||||||
.ToImmutableDictionary(x => x.QuestId, x => x);
|
.ToImmutableDictionary(x => x.QuestId, x => x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuestInfo GetQuestInfo(ushort questId)
|
public QuestInfo GetQuestInfo(IId id)
|
||||||
|
{
|
||||||
|
if (id is QuestId questId)
|
||||||
|
return GetQuestInfo(questId);
|
||||||
|
|
||||||
|
throw new ArgumentException("Invalid id", nameof(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public QuestInfo GetQuestInfo(QuestId questId)
|
||||||
{
|
{
|
||||||
return _quests[questId] ?? throw new ArgumentOutOfRangeException(nameof(questId));
|
return _quests[questId] ?? throw new ArgumentOutOfRangeException(nameof(questId));
|
||||||
}
|
}
|
||||||
|
@ -89,37 +89,36 @@ internal sealed unsafe class GameFunctions
|
|||||||
|
|
||||||
public DateTime ReturnRequestedAt { get; set; } = DateTime.MinValue;
|
public DateTime ReturnRequestedAt { get; set; } = DateTime.MinValue;
|
||||||
|
|
||||||
public (ushort CurrentQuest, byte Sequence) GetCurrentQuest()
|
public (IId? CurrentQuest, byte Sequence) GetCurrentQuest()
|
||||||
{
|
{
|
||||||
var (currentQuest, sequence) = GetCurrentQuestInternal();
|
var (currentQuest, sequence) = GetCurrentQuestInternal();
|
||||||
QuestManager* questManager = QuestManager.Instance();
|
|
||||||
PlayerState* playerState = PlayerState.Instance();
|
PlayerState* playerState = PlayerState.Instance();
|
||||||
|
|
||||||
if (currentQuest == 0)
|
if (currentQuest == null || currentQuest.Value == 0)
|
||||||
{
|
{
|
||||||
if (_clientState.TerritoryType == 181) // Starting in Limsa
|
if (_clientState.TerritoryType == 181) // Starting in Limsa
|
||||||
return (107, 0);
|
return (new QuestId(107), 0);
|
||||||
if (_clientState.TerritoryType == 182) // Starting in Ul'dah
|
if (_clientState.TerritoryType == 182) // Starting in Ul'dah
|
||||||
return (594, 0);
|
return (new QuestId(594), 0);
|
||||||
if (_clientState.TerritoryType == 183) // Starting in Gridania
|
if (_clientState.TerritoryType == 183) // Starting in Gridania
|
||||||
return (39, 0);
|
return (new QuestId(39), 0);
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
else if (currentQuest == 681)
|
else if (currentQuest.Value == 681)
|
||||||
{
|
{
|
||||||
// if we have already picked up the GC quest, just return the progress for it
|
// if we have already picked up the GC quest, just return the progress for it
|
||||||
if (questManager->IsQuestAccepted(currentQuest) || QuestManager.IsQuestComplete(currentQuest))
|
if (IsQuestAccepted(currentQuest) || IsQuestComplete(currentQuest))
|
||||||
return (currentQuest, sequence);
|
return (currentQuest, sequence);
|
||||||
|
|
||||||
// The company you keep...
|
// The company you keep...
|
||||||
return _configuration.General.GrandCompany switch
|
return _configuration.General.GrandCompany switch
|
||||||
{
|
{
|
||||||
GrandCompany.TwinAdder => (680, 0),
|
GrandCompany.TwinAdder => (new QuestId(680), 0),
|
||||||
GrandCompany.Maelstrom => (681, 0),
|
GrandCompany.Maelstrom => (new QuestId(681), 0),
|
||||||
_ => default
|
_ => default
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (currentQuest == 3856 && !playerState->IsMountUnlocked(1)) // we come in peace
|
else if (currentQuest.Value == 3856 && !playerState->IsMountUnlocked(1)) // we come in peace
|
||||||
{
|
{
|
||||||
ushort chocoboQuest = (GrandCompany)playerState->GrandCompany switch
|
ushort chocoboQuest = (GrandCompany)playerState->GrandCompany switch
|
||||||
{
|
{
|
||||||
@ -129,20 +128,20 @@ internal sealed unsafe class GameFunctions
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (chocoboQuest != 0 && !QuestManager.IsQuestComplete(chocoboQuest))
|
if (chocoboQuest != 0 && !QuestManager.IsQuestComplete(chocoboQuest))
|
||||||
return (chocoboQuest, QuestManager.GetQuestSequence(chocoboQuest));
|
return (new QuestId(chocoboQuest), QuestManager.GetQuestSequence(chocoboQuest));
|
||||||
}
|
}
|
||||||
else if (currentQuest == 801)
|
else if (currentQuest.Value == 801)
|
||||||
{
|
{
|
||||||
// skeletons in her closet, finish 'broadening horizons' to unlock the white wolf gate
|
// skeletons in her closet, finish 'broadening horizons' to unlock the white wolf gate
|
||||||
ushort broadeningHorizons = 802;
|
QuestId broadeningHorizons = new QuestId(802);
|
||||||
if (questManager->IsQuestAccepted(broadeningHorizons))
|
if (IsQuestAccepted(broadeningHorizons))
|
||||||
return (broadeningHorizons, QuestManager.GetQuestSequence(broadeningHorizons));
|
return (broadeningHorizons, QuestManager.GetQuestSequence(broadeningHorizons.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (currentQuest, sequence);
|
return (currentQuest, sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
public (ushort CurrentQuest, byte Sequence) GetCurrentQuestInternal()
|
public (IId? CurrentQuest, byte Sequence) GetCurrentQuestInternal()
|
||||||
{
|
{
|
||||||
var questManager = QuestManager.Instance();
|
var questManager = QuestManager.Instance();
|
||||||
if (questManager != null)
|
if (questManager != null)
|
||||||
@ -150,7 +149,7 @@ internal sealed unsafe class GameFunctions
|
|||||||
// always prioritize accepting MSQ quests, to make sure we don't turn in one MSQ quest and then go off to do
|
// always prioritize accepting MSQ quests, to make sure we don't turn in one MSQ quest and then go off to do
|
||||||
// side quests until the end of time.
|
// side quests until the end of time.
|
||||||
var msqQuest = GetMainScenarioQuest(questManager);
|
var msqQuest = GetMainScenarioQuest(questManager);
|
||||||
if (msqQuest.CurrentQuest != 0 && _questRegistry.IsKnownQuest(msqQuest.CurrentQuest))
|
if (msqQuest.CurrentQuest is { Value: not 0 } && _questRegistry.IsKnownQuest(msqQuest.CurrentQuest))
|
||||||
return msqQuest;
|
return msqQuest;
|
||||||
|
|
||||||
// Use the quests in the same order as they're shown in the to-do list, e.g. if the MSQ is the first item,
|
// Use the quests in the same order as they're shown in the to-do list, e.g. if the MSQ is the first item,
|
||||||
@ -159,7 +158,7 @@ internal sealed unsafe class GameFunctions
|
|||||||
// If no quests are marked as 'priority', accepting a new quest adds it to the top of the list.
|
// If no quests are marked as 'priority', accepting a new quest adds it to the top of the list.
|
||||||
for (int i = questManager->TrackedQuests.Length - 1; i >= 0; --i)
|
for (int i = questManager->TrackedQuests.Length - 1; i >= 0; --i)
|
||||||
{
|
{
|
||||||
ushort currentQuest;
|
IId currentQuest;
|
||||||
var trackedQuest = questManager->TrackedQuests[i];
|
var trackedQuest = questManager->TrackedQuests[i];
|
||||||
switch (trackedQuest.QuestType)
|
switch (trackedQuest.QuestType)
|
||||||
{
|
{
|
||||||
@ -167,12 +166,12 @@ internal sealed unsafe class GameFunctions
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
case 1: // normal quest
|
case 1: // normal quest
|
||||||
currentQuest = questManager->NormalQuests[trackedQuest.Index].QuestId;
|
currentQuest = new QuestId(questManager->NormalQuests[trackedQuest.Index].QuestId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_questRegistry.IsKnownQuest(currentQuest))
|
if (_questRegistry.IsKnownQuest(currentQuest))
|
||||||
return (currentQuest, QuestManager.GetQuestSequence(currentQuest));
|
return (currentQuest, QuestManager.GetQuestSequence(currentQuest.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we know no quest of those currently in the to-do list, just do MSQ
|
// if we know no quest of those currently in the to-do list, just do MSQ
|
||||||
@ -182,7 +181,7 @@ internal sealed unsafe class GameFunctions
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private (ushort CurrentQuest, byte Sequence) GetMainScenarioQuest(QuestManager* questManager)
|
private (QuestId? CurrentQuest, byte Sequence) GetMainScenarioQuest(QuestManager* questManager)
|
||||||
{
|
{
|
||||||
if (QuestManager.IsQuestComplete(3759)) // Memories Rekindled
|
if (QuestManager.IsQuestComplete(3759)) // Memories Rekindled
|
||||||
{
|
{
|
||||||
@ -202,7 +201,7 @@ internal sealed unsafe class GameFunctions
|
|||||||
// redoHud+44 is chapter
|
// redoHud+44 is chapter
|
||||||
// redoHud+46 is quest
|
// redoHud+46 is quest
|
||||||
ushort questId = MemoryHelper.Read<ushort>((nint)questRedoHud + 46);
|
ushort questId = MemoryHelper.Read<ushort>((nint)questRedoHud + 46);
|
||||||
return (questId, QuestManager.GetQuestSequence(questId));
|
return (new QuestId(questId), QuestManager.GetQuestSequence(questId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,12 +213,12 @@ internal sealed unsafe class GameFunctions
|
|||||||
if (scenarioTree->Data == null)
|
if (scenarioTree->Data == null)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
ushort currentQuest = scenarioTree->Data->CurrentScenarioQuest;
|
QuestId currentQuest = new QuestId(scenarioTree->Data->CurrentScenarioQuest);
|
||||||
if (currentQuest == 0)
|
if (currentQuest.Value == 0)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
// if the MSQ is hidden, we generally ignore it
|
// if the MSQ is hidden, we generally ignore it
|
||||||
if (questManager->IsQuestAccepted(currentQuest) && questManager->GetQuestById(currentQuest)->IsHidden)
|
if (IsQuestAccepted(currentQuest) && questManager->GetQuestById(currentQuest.Value)->IsHidden)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
// it can sometimes happen (although this isn't reliably reproducible) that the quest returned here
|
// it can sometimes happen (although this isn't reliably reproducible) that the quest returned here
|
||||||
@ -234,16 +233,23 @@ internal sealed unsafe class GameFunctions
|
|||||||
&& quest.Info.Level > currentLevel)
|
&& quest.Info.Level > currentLevel)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
return (currentQuest, QuestManager.GetQuestSequence(currentQuest));
|
return (currentQuest, QuestManager.GetQuestSequence(currentQuest.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuestWork? GetQuestEx(ushort questId)
|
public QuestWork? GetQuestEx(QuestId questId)
|
||||||
{
|
{
|
||||||
QuestWork* questWork = QuestManager.Instance()->GetQuestById(questId);
|
QuestWork* questWork = QuestManager.Instance()->GetQuestById(questId.Value);
|
||||||
return questWork != null ? *questWork : null;
|
return questWork != null ? *questWork : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsReadyToAcceptQuest(ushort questId)
|
public bool IsReadyToAcceptQuest(IId id)
|
||||||
|
{
|
||||||
|
if (id is QuestId questId)
|
||||||
|
return IsReadyToAcceptQuest(questId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadyToAcceptQuest(QuestId questId)
|
||||||
{
|
{
|
||||||
_questRegistry.TryGetQuest(questId, out var quest);
|
_questRegistry.TryGetQuest(questId, out var quest);
|
||||||
if (quest is { Info.IsRepeatable: true })
|
if (quest is { Info.IsRepeatable: true })
|
||||||
@ -268,29 +274,50 @@ internal sealed unsafe class GameFunctions
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsQuestAcceptedOrComplete(ushort questId)
|
public bool IsQuestAcceptedOrComplete(IId questId)
|
||||||
{
|
{
|
||||||
return IsQuestComplete(questId) || IsQuestAccepted(questId);
|
return IsQuestComplete(questId) || IsQuestAccepted(questId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsQuestAccepted(ushort questId)
|
public bool IsQuestAccepted(IId id)
|
||||||
|
{
|
||||||
|
if (id is QuestId questId)
|
||||||
|
return IsQuestAccepted(questId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsQuestAccepted(QuestId questId)
|
||||||
{
|
{
|
||||||
QuestManager* questManager = QuestManager.Instance();
|
QuestManager* questManager = QuestManager.Instance();
|
||||||
return questManager->IsQuestAccepted(questId);
|
return questManager->IsQuestAccepted(questId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsQuestComplete(IId id)
|
||||||
|
{
|
||||||
|
if (id is QuestId questId)
|
||||||
|
return IsQuestComplete(questId);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("Performance", "CA1822")]
|
[SuppressMessage("Performance", "CA1822")]
|
||||||
public bool IsQuestComplete(ushort questId)
|
public bool IsQuestComplete(QuestId questId)
|
||||||
{
|
{
|
||||||
return QuestManager.IsQuestComplete(questId);
|
return QuestManager.IsQuestComplete(questId.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsQuestLocked(ushort questId, ushort? extraCompletedQuest = null)
|
public bool IsQuestLocked(IId id, IId? extraCompletedQuest = null)
|
||||||
|
{
|
||||||
|
if (id is QuestId questId)
|
||||||
|
return IsQuestLocked(questId, extraCompletedQuest);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsQuestLocked(QuestId questId, IId? extraCompletedQuest = null)
|
||||||
{
|
{
|
||||||
var questInfo = _questData.GetQuestInfo(questId);
|
var questInfo = _questData.GetQuestInfo(questId);
|
||||||
if (questInfo.QuestLocks.Count > 0)
|
if (questInfo.QuestLocks.Count > 0)
|
||||||
{
|
{
|
||||||
var completedQuests = questInfo.QuestLocks.Count(x => IsQuestComplete(x) || x == extraCompletedQuest);
|
var completedQuests = questInfo.QuestLocks.Count(x => IsQuestComplete(x) || x.Equals(extraCompletedQuest));
|
||||||
if (questInfo.QuestLockJoin == QuestInfo.QuestJoin.All && questInfo.QuestLocks.Count == completedQuests)
|
if (questInfo.QuestLockJoin == QuestInfo.QuestJoin.All && questInfo.QuestLocks.Count == completedQuests)
|
||||||
return true;
|
return true;
|
||||||
else if (questInfo.QuestLockJoin == QuestInfo.QuestJoin.AtLeastOne && completedQuests > 0)
|
else if (questInfo.QuestLockJoin == QuestInfo.QuestJoin.AtLeastOne && completedQuests > 0)
|
||||||
@ -303,12 +330,12 @@ internal sealed unsafe class GameFunctions
|
|||||||
return !HasCompletedPreviousQuests(questInfo, extraCompletedQuest) || !HasCompletedPreviousInstances(questInfo);
|
return !HasCompletedPreviousQuests(questInfo, extraCompletedQuest) || !HasCompletedPreviousInstances(questInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasCompletedPreviousQuests(QuestInfo questInfo, ushort? extraCompletedQuest)
|
private bool HasCompletedPreviousQuests(QuestInfo questInfo, IId? extraCompletedQuest)
|
||||||
{
|
{
|
||||||
if (questInfo.PreviousQuests.Count == 0)
|
if (questInfo.PreviousQuests.Count == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var completedQuests = questInfo.PreviousQuests.Count(x => IsQuestComplete(x) || x == extraCompletedQuest);
|
var completedQuests = questInfo.PreviousQuests.Count(x => IsQuestComplete(x) || x.Equals(extraCompletedQuest));
|
||||||
if (questInfo.PreviousQuestJoin == QuestInfo.QuestJoin.All &&
|
if (questInfo.PreviousQuestJoin == QuestInfo.QuestJoin.All &&
|
||||||
questInfo.PreviousQuests.Count == completedQuests)
|
questInfo.PreviousQuests.Count == completedQuests)
|
||||||
return true;
|
return true;
|
||||||
@ -388,7 +415,7 @@ internal sealed unsafe class GameFunctions
|
|||||||
if (_configuration.Advanced.NeverFly)
|
if (_configuration.Advanced.NeverFly)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (IsQuestAccepted(3304) && _condition[ConditionFlag.Mounted])
|
if (IsQuestAccepted(new QuestId(3304)) && _condition[ConditionFlag.Mounted])
|
||||||
{
|
{
|
||||||
BattleChara* battleChara = (BattleChara*)(_clientState.LocalPlayer?.Address ?? 0);
|
BattleChara* battleChara = (BattleChara*)(_clientState.LocalPlayer?.Address ?? 0);
|
||||||
if (battleChara != null && battleChara->Mount.MountId == 198) // special quest amaro, not the normal one
|
if (battleChara != null && battleChara->Mount.MountId == 198) // special quest amaro, not the normal one
|
||||||
@ -680,7 +707,7 @@ internal sealed unsafe class GameFunctions
|
|||||||
if (excelSheetName == null)
|
if (excelSheetName == null)
|
||||||
{
|
{
|
||||||
var questRow =
|
var questRow =
|
||||||
_dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets2.Quest>()!.GetRow((uint)currentQuest.QuestId +
|
_dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets2.Quest>()!.GetRow((uint)currentQuest.QuestId.Value +
|
||||||
0x10000);
|
0x10000);
|
||||||
if (questRow == null)
|
if (questRow == null)
|
||||||
{
|
{
|
||||||
@ -688,7 +715,7 @@ internal sealed unsafe class GameFunctions
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
excelSheetName = $"quest/{(currentQuest.QuestId / 100):000}/{questRow.Id}";
|
excelSheetName = $"quest/{(currentQuest.QuestId.Value / 100):000}/{questRow.Id}";
|
||||||
}
|
}
|
||||||
|
|
||||||
var excelSheet = _dataManager.Excel.GetSheet<QuestDialogueText>(excelSheetName);
|
var excelSheet = _dataManager.Excel.GetSheet<QuestDialogueText>(excelSheetName);
|
||||||
|
@ -6,7 +6,7 @@ namespace Questionable.Model;
|
|||||||
|
|
||||||
internal sealed class Quest
|
internal sealed class Quest
|
||||||
{
|
{
|
||||||
public required ushort QuestId { get; init; }
|
public required IId QuestId { get; init; }
|
||||||
public required QuestRoot Root { get; init; }
|
public required QuestRoot Root { get; init; }
|
||||||
public required QuestInfo Info { get; init; }
|
public required QuestInfo Info { get; init; }
|
||||||
public required bool ReadOnly { get; init; }
|
public required bool ReadOnly { get; init; }
|
||||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
using ExcelQuest = Lumina.Excel.GeneratedSheets.Quest;
|
using ExcelQuest = Lumina.Excel.GeneratedSheets.Quest;
|
||||||
|
|
||||||
namespace Questionable.Model;
|
namespace Questionable.Model;
|
||||||
@ -13,9 +14,9 @@ internal sealed class QuestInfo
|
|||||||
{
|
{
|
||||||
public QuestInfo(ExcelQuest quest)
|
public QuestInfo(ExcelQuest quest)
|
||||||
{
|
{
|
||||||
QuestId = (ushort)(quest.RowId & 0xFFFF);
|
QuestId = new QuestId((ushort)(quest.RowId & 0xFFFF));
|
||||||
|
|
||||||
string suffix = QuestId switch
|
string suffix = QuestId.Value switch
|
||||||
{
|
{
|
||||||
85 => " (Lancer)",
|
85 => " (Lancer)",
|
||||||
108 => " (Marauder)",
|
108 => " (Marauder)",
|
||||||
@ -34,9 +35,15 @@ internal sealed class QuestInfo
|
|||||||
Level = quest.ClassJobLevel0;
|
Level = quest.ClassJobLevel0;
|
||||||
IssuerDataId = quest.IssuerStart;
|
IssuerDataId = quest.IssuerStart;
|
||||||
IsRepeatable = quest.IsRepeatable;
|
IsRepeatable = quest.IsRepeatable;
|
||||||
PreviousQuests = quest.PreviousQuest.Select(x => (ushort)(x.Row & 0xFFFF)).Where(x => x != 0).ToImmutableList();
|
PreviousQuests = quest.PreviousQuest
|
||||||
|
.Select(x => new QuestId((ushort)(x.Row & 0xFFFF)))
|
||||||
|
.Where(x => x.Value != 0)
|
||||||
|
.ToImmutableList();
|
||||||
PreviousQuestJoin = (QuestJoin)quest.PreviousQuestJoin;
|
PreviousQuestJoin = (QuestJoin)quest.PreviousQuestJoin;
|
||||||
QuestLocks = quest.QuestLock.Select(x => (ushort)(x.Row & 0xFFFFF)).Where(x => x != 0).ToImmutableList();
|
QuestLocks = quest.QuestLock
|
||||||
|
.Select(x => new QuestId((ushort)(x.Row & 0xFFFFF)))
|
||||||
|
.Where(x => x.Value != 0)
|
||||||
|
.ToImmutableList();
|
||||||
QuestLockJoin = (QuestJoin)quest.QuestLockJoin;
|
QuestLockJoin = (QuestJoin)quest.QuestLockJoin;
|
||||||
JournalGenre = quest.JournalGenre?.Row;
|
JournalGenre = quest.JournalGenre?.Row;
|
||||||
SortKey = quest.SortKey;
|
SortKey = quest.SortKey;
|
||||||
@ -49,14 +56,14 @@ internal sealed class QuestInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ushort QuestId { get; }
|
public QuestId QuestId { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public ushort Level { get; }
|
public ushort Level { get; }
|
||||||
public uint IssuerDataId { get; }
|
public uint IssuerDataId { get; }
|
||||||
public bool IsRepeatable { get; }
|
public bool IsRepeatable { get; }
|
||||||
public ImmutableList<ushort> PreviousQuests { get; }
|
public ImmutableList<QuestId> PreviousQuests { get; }
|
||||||
public QuestJoin PreviousQuestJoin { get; }
|
public QuestJoin PreviousQuestJoin { get; }
|
||||||
public ImmutableList<ushort> QuestLocks { get; }
|
public ImmutableList<QuestId> QuestLocks { get; }
|
||||||
public QuestJoin QuestLockJoin { get; }
|
public QuestJoin QuestLockJoin { get; }
|
||||||
public List<ushort> PreviousInstanceContent { get; }
|
public List<ushort> PreviousInstanceContent { get; }
|
||||||
public QuestJoin PreviousInstanceContentJoin { get; }
|
public QuestJoin PreviousInstanceContentJoin { get; }
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using Questionable.Model;
|
using Questionable.Model;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
|
|
||||||
namespace Questionable.Validation;
|
namespace Questionable.Validation;
|
||||||
|
|
||||||
internal sealed record ValidationIssue
|
internal sealed record ValidationIssue
|
||||||
{
|
{
|
||||||
public required ushort? QuestId { get; init; }
|
public required IId? QuestId { get; init; }
|
||||||
public required byte? Sequence { get; init; }
|
public required byte? Sequence { get; init; }
|
||||||
public required int? Step { get; init; }
|
public required int? Step { get; init; }
|
||||||
public EBeastTribe BeastTribe { get; init; } = EBeastTribe.None;
|
public EBeastTribe BeastTribe { get; init; } = EBeastTribe.None;
|
||||||
|
@ -23,7 +23,7 @@ internal sealed class AethernetShortcutValidator : IQuestValidator
|
|||||||
.Cast<ValidationIssue>();
|
.Cast<ValidationIssue>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ValidationIssue? Validate(ushort questId, int sequenceNo, int stepId, AethernetShortcut? aethernetShortcut)
|
private ValidationIssue? Validate(IId questId, int sequenceNo, int stepId, AethernetShortcut? aethernetShortcut)
|
||||||
{
|
{
|
||||||
if (aethernetShortcut == null)
|
if (aethernetShortcut == null)
|
||||||
return null;
|
return null;
|
||||||
|
@ -4,13 +4,14 @@ using System.Globalization;
|
|||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using Json.Schema;
|
using Json.Schema;
|
||||||
using Questionable.Model;
|
using Questionable.Model;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
using Questionable.QuestPaths;
|
using Questionable.QuestPaths;
|
||||||
|
|
||||||
namespace Questionable.Validation.Validators;
|
namespace Questionable.Validation.Validators;
|
||||||
|
|
||||||
internal sealed class JsonSchemaValidator : IQuestValidator
|
internal sealed class JsonSchemaValidator : IQuestValidator
|
||||||
{
|
{
|
||||||
private readonly Dictionary<ushort, JsonNode> _questNodes = new();
|
private readonly Dictionary<IId, JsonNode> _questNodes = new();
|
||||||
private JsonSchema? _questSchema;
|
private JsonSchema? _questSchema;
|
||||||
|
|
||||||
public JsonSchemaValidator()
|
public JsonSchemaValidator()
|
||||||
@ -46,7 +47,7 @@ internal sealed class JsonSchemaValidator : IQuestValidator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Enqueue(ushort questId, JsonNode questNode) => _questNodes[questId] = questNode;
|
public void Enqueue(IId questId, JsonNode questNode) => _questNodes[questId] = questNode;
|
||||||
|
|
||||||
public void Reset() => _questNodes.Clear();
|
public void Reset() => _questNodes.Clear();
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ internal sealed class DebugOverlay : Window
|
|||||||
IsOpen = true;
|
IsOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ushort? HighlightedQuest { get; set; }
|
public IId? HighlightedQuest { get; set; }
|
||||||
|
|
||||||
public override bool DrawConditions() => _configuration.Advanced.DebugOverlay;
|
public override bool DrawConditions() => _configuration.Advanced.DebugOverlay;
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ internal sealed class DebugOverlay : Window
|
|||||||
|
|
||||||
private void DrawHighlightedQuest()
|
private void DrawHighlightedQuest()
|
||||||
{
|
{
|
||||||
if (HighlightedQuest == null || !_questRegistry.TryGetQuest(HighlightedQuest.Value, out var quest))
|
if (HighlightedQuest == null || !_questRegistry.TryGetQuest(HighlightedQuest, out var quest))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var sequence in quest.Root.QuestSequence)
|
foreach (var sequence in quest.Root.QuestSequence)
|
||||||
|
@ -201,8 +201,7 @@ internal sealed class JournalProgressWindow : LWindow, IDisposable
|
|||||||
|
|
||||||
if (ImGui.IsItemClicked() && _commandManager.Commands.TryGetValue("/questinfo", out var commandInfo))
|
if (ImGui.IsItemClicked() && _commandManager.Commands.TryGetValue("/questinfo", out var commandInfo))
|
||||||
{
|
{
|
||||||
_commandManager.DispatchCommand("/questinfo",
|
_commandManager.DispatchCommand("/questinfo", questInfo.QuestId.ToString(), commandInfo);
|
||||||
questInfo.QuestId.ToString(CultureInfo.InvariantCulture), commandInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
|
@ -4,16 +4,19 @@ using Dalamud.Interface.Utility.Raii;
|
|||||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Common.Math;
|
using FFXIVClientStructs.FFXIV.Common.Math;
|
||||||
using Questionable.Data;
|
using Questionable.Data;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
|
|
||||||
namespace Questionable.Windows.QuestComponents;
|
namespace Questionable.Windows.QuestComponents;
|
||||||
|
|
||||||
internal sealed class ARealmRebornComponent
|
internal sealed class ARealmRebornComponent
|
||||||
{
|
{
|
||||||
private const ushort ATimeForEveryPurpose = 425;
|
private static readonly QuestId ATimeForEveryPurpose = new(425);
|
||||||
private const ushort TheUltimateWeapon = 524;
|
private static readonly QuestId TheUltimateWeapon = new(524);
|
||||||
private const ushort GoodIntentions = 363;
|
private static readonly QuestId GoodIntentions = new(363);
|
||||||
private static readonly ushort[] RequiredPrimalInstances = [20004, 20006, 20005];
|
private static readonly ushort[] RequiredPrimalInstances = [20004, 20006, 20005];
|
||||||
private static readonly ushort[] RequiredAllianceRaidQuests = [1709, 1200, 1201, 1202, 1203, 1474, 494, 495];
|
|
||||||
|
private static readonly QuestId[] RequiredAllianceRaidQuests =
|
||||||
|
[new(1709), new(1200), new(1201), new(1202), new(1203), new(1474), new(494), new(495)];
|
||||||
|
|
||||||
private readonly GameFunctions _gameFunctions;
|
private readonly GameFunctions _gameFunctions;
|
||||||
private readonly QuestData _questData;
|
private readonly QuestData _questData;
|
||||||
|
@ -151,7 +151,10 @@ internal sealed class ActiveQuestComponent
|
|||||||
|
|
||||||
private QuestWork? DrawQuestWork(QuestController.QuestProgress currentQuest)
|
private QuestWork? DrawQuestWork(QuestController.QuestProgress currentQuest)
|
||||||
{
|
{
|
||||||
var questWork = _gameFunctions.GetQuestEx(currentQuest.Quest.QuestId);
|
if (currentQuest.Quest.QuestId is not QuestId questId)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var questWork = _gameFunctions.GetQuestEx(questId);
|
||||||
if (questWork != null)
|
if (questWork != null)
|
||||||
{
|
{
|
||||||
Vector4 color;
|
Vector4 color;
|
||||||
@ -271,7 +274,7 @@ internal sealed class ActiveQuestComponent
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Atlas))
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Atlas))
|
||||||
_commandManager.DispatchCommand("/questinfo",
|
_commandManager.DispatchCommand("/questinfo",
|
||||||
currentQuest.Quest.QuestId.ToString(CultureInfo.InvariantCulture), commandInfo);
|
currentQuest.Quest.QuestId.ToString() ?? string.Empty, commandInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool autoAcceptNextQuest = _configuration.General.AutoAcceptNextQuest;
|
bool autoAcceptNextQuest = _configuration.General.AutoAcceptNextQuest;
|
||||||
|
@ -110,7 +110,7 @@ internal sealed class QuestSelectionWindow : LWindow
|
|||||||
|
|
||||||
foreach (var unacceptedQuest in Map.Instance()->UnacceptedQuestMarkers)
|
foreach (var unacceptedQuest in Map.Instance()->UnacceptedQuestMarkers)
|
||||||
{
|
{
|
||||||
ushort questId = (ushort)(unacceptedQuest.ObjectiveId & 0xFFFF);
|
QuestId questId = new QuestId((ushort)(unacceptedQuest.ObjectiveId & 0xFFFF));
|
||||||
if (_quests.All(q => q.QuestId != questId))
|
if (_quests.All(q => q.QuestId != questId))
|
||||||
_quests.Add(_questData.GetQuestInfo(questId));
|
_quests.Add(_questData.GetQuestInfo(questId));
|
||||||
}
|
}
|
||||||
@ -161,7 +161,7 @@ internal sealed class QuestSelectionWindow : LWindow
|
|||||||
{
|
{
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
|
|
||||||
string questId = quest.QuestId.ToString(CultureInfo.InvariantCulture);
|
string questId = quest.QuestId.ToString();
|
||||||
bool isKnownQuest = _questRegistry.TryGetQuest(quest.QuestId, out var knownQuest);
|
bool isKnownQuest = _questRegistry.TryGetQuest(quest.QuestId, out var knownQuest);
|
||||||
|
|
||||||
if (ImGui.TableNextColumn())
|
if (ImGui.TableNextColumn())
|
||||||
|
@ -56,11 +56,11 @@ internal sealed class QuestValidationWindow : LWindow
|
|||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
|
|
||||||
if (ImGui.TableNextColumn())
|
if (ImGui.TableNextColumn())
|
||||||
ImGui.TextUnformatted(validationIssue.QuestId?.ToString(CultureInfo.InvariantCulture) ?? string.Empty);
|
ImGui.TextUnformatted(validationIssue.QuestId?.ToString() ?? string.Empty);
|
||||||
|
|
||||||
if (ImGui.TableNextColumn())
|
if (ImGui.TableNextColumn())
|
||||||
ImGui.TextUnformatted(validationIssue.QuestId != null
|
ImGui.TextUnformatted(validationIssue.QuestId != null
|
||||||
? _questData.GetQuestInfo(validationIssue.QuestId.Value).Name
|
? _questData.GetQuestInfo(validationIssue.QuestId).Name
|
||||||
: validationIssue.BeastTribe.ToString());
|
: validationIssue.BeastTribe.ToString());
|
||||||
|
|
||||||
if (ImGui.TableNextColumn())
|
if (ImGui.TableNextColumn())
|
||||||
|
@ -4,6 +4,7 @@ using Dalamud.Interface.Colors;
|
|||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
using Questionable.Model.Questing;
|
||||||
|
|
||||||
namespace Questionable.Windows;
|
namespace Questionable.Windows;
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ internal sealed class UiUtils
|
|||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
public (Vector4 color, FontAwesomeIcon icon, string status) GetQuestStyle(ushort questId)
|
public (Vector4 color, FontAwesomeIcon icon, string status) GetQuestStyle(IId questId)
|
||||||
{
|
{
|
||||||
if (_gameFunctions.IsQuestAccepted(questId))
|
if (_gameFunctions.IsQuestAccepted(questId))
|
||||||
return (ImGuiColors.DalamudYellow, FontAwesomeIcon.Running, "Active");
|
return (ImGuiColors.DalamudYellow, FontAwesomeIcon.Running, "Active");
|
||||||
|
Loading…
Reference in New Issue
Block a user