forked from liza/Questionable
Migrate questIds to IId
This commit is contained in:
parent
ee2b49f566
commit
36a0f72bd9
@ -162,6 +162,7 @@ public class QuestSourceGenerator : ISourceGenerator
|
||||
SyntaxNodeList(
|
||||
AssignmentList(nameof(QuestRoot.Author), quest.Author)
|
||||
.AsSyntaxNodeOrToken(),
|
||||
Assignment(nameof(QuestRoot.Disabled), quest.Disabled, false).AsSyntaxNodeOrToken(),
|
||||
Assignment(nameof(QuestRoot.Comment), quest.Comment, null)
|
||||
.AsSyntaxNodeOrToken(),
|
||||
AssignmentList(nameof(QuestRoot.TerritoryBlacklist),
|
||||
@ -304,7 +305,8 @@ public class QuestSourceGenerator : ISourceGenerator
|
||||
Assignment(nameof(QuestStep.ContentFinderConditionId),
|
||||
step.ContentFinderConditionId, emptyStep.ContentFinderConditionId)
|
||||
.AsSyntaxNodeOrToken(),
|
||||
Assignment(nameof(QuestStep.SkipConditions), step.SkipConditions, emptyStep.SkipConditions)
|
||||
Assignment(nameof(QuestStep.SkipConditions), step.SkipConditions,
|
||||
emptyStep.SkipConditions)
|
||||
.AsSyntaxNodeOrToken(),
|
||||
AssignmentList(nameof(QuestStep.RequiredQuestVariables),
|
||||
step.RequiredQuestVariables)
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Microsoft.CodeAnalysis;
|
||||
@ -57,6 +56,24 @@ public static class RoslynShortcuts
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
IdentifierName(value.GetType().Name),
|
||||
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)
|
||||
{
|
||||
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,
|
||||
"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>();
|
||||
|
||||
// TODO: Not implemented
|
||||
public ushort? PickUpQuestId { get; set; }
|
||||
[JsonConverter(typeof(IdConverter))]
|
||||
public IId? PickUpQuestId { get; set; }
|
||||
|
||||
public ushort? TurnInQuestId { get; set; }
|
||||
public ushort? NextQuestId { get; set; }
|
||||
[JsonConverter(typeof(IdConverter))]
|
||||
public IId? TurnInQuestId { get; set; }
|
||||
|
||||
[JsonConverter(typeof(IdConverter))]
|
||||
public IId? NextQuestId { get; set; }
|
||||
|
||||
[JsonConstructor]
|
||||
public QuestStep()
|
||||
|
@ -13,8 +13,8 @@ public sealed class SkipStepConditions
|
||||
public List<ushort> InTerritory { get; set; } = new();
|
||||
public List<ushort> NotInTerritory { get; set; } = new();
|
||||
public SkipItemConditions? Item { get; set; }
|
||||
public List<ushort> QuestsAccepted { get; set; } = new();
|
||||
public List<ushort> QuestsCompleted { get; set; } = new();
|
||||
public List<IId> QuestsAccepted { get; set; } = new();
|
||||
public List<IId> QuestsCompleted { get; set; } = new();
|
||||
public EExtraSkipCondition? ExtraCondition { get; set; }
|
||||
|
||||
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,
|
||||
questWork.Value))
|
||||
{
|
||||
@ -303,7 +303,7 @@ internal sealed class CombatController : IDisposable
|
||||
|
||||
public sealed class CombatData
|
||||
{
|
||||
public required ushort QuestId { get; init; }
|
||||
public required IId QuestId { get; init; }
|
||||
public required EEnemySpawnType SpawnType { get; init; }
|
||||
public required List<uint> KillEnemyDataIds { get; init; }
|
||||
public required List<ComplexCombatData> ComplexCombatDatas { get; init; }
|
||||
|
@ -4,6 +4,7 @@ using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
using Questionable.Windows;
|
||||
using Questionable.Windows.QuestComponents;
|
||||
|
||||
@ -127,11 +128,11 @@ internal sealed class CommandHandler : IDisposable
|
||||
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}).");
|
||||
}
|
||||
else
|
||||
@ -146,11 +147,11 @@ internal sealed class CommandHandler : IDisposable
|
||||
|
||||
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.");
|
||||
else if (_questRegistry.TryGetQuest(questId, out Quest? quest))
|
||||
else if (_questRegistry.TryGetQuest(Id.From(questId), out Quest? quest))
|
||||
{
|
||||
_questController.SetNextQuest(quest);
|
||||
_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 (_questRegistry.TryGetQuest(questId, out Quest? quest))
|
||||
if (_questRegistry.TryGetQuest(Id.From(questId), out Quest? quest))
|
||||
{
|
||||
_questController.SimulateQuest(quest);
|
||||
_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)
|
||||
{
|
||||
if (_questController.StartedQuest?.Quest.QuestId == 4526)
|
||||
if (_questController.StartedQuest?.Quest.QuestId.Value == 4526)
|
||||
{
|
||||
_logger.LogInformation("Closing Unending Codex");
|
||||
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
||||
@ -610,7 +610,7 @@ internal sealed class GameUiController : IDisposable
|
||||
|
||||
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");
|
||||
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
||||
@ -623,7 +623,7 @@ internal sealed class GameUiController : IDisposable
|
||||
/// </summary>
|
||||
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");
|
||||
AtkUnitBase* addon = (AtkUnitBase*)args.Addon;
|
||||
|
@ -209,8 +209,8 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
||||
}
|
||||
else
|
||||
{
|
||||
(ushort currentQuestId, currentSequence) = _gameFunctions.GetCurrentQuest();
|
||||
if (currentQuestId == 0)
|
||||
(IId? currentQuestId, currentSequence) = _gameFunctions.GetCurrentQuest();
|
||||
if (currentQuestId == null || currentQuestId.Value == 0)
|
||||
{
|
||||
if (_startedQuest != null)
|
||||
{
|
||||
@ -330,7 +330,7 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
||||
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)
|
||||
{
|
||||
@ -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)
|
||||
{
|
||||
@ -609,8 +609,9 @@ internal sealed class QuestController : MiniTaskController<QuestController>
|
||||
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))
|
||||
{
|
||||
SetNextQuest(quest);
|
||||
|
@ -28,7 +28,7 @@ internal sealed class QuestRegistry
|
||||
private readonly ILogger<QuestRegistry> _logger;
|
||||
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,
|
||||
QuestValidator questValidator, JsonSchemaValidator jsonSchemaValidator,
|
||||
@ -91,12 +91,12 @@ internal sealed class QuestRegistry
|
||||
{
|
||||
Quest quest = new()
|
||||
{
|
||||
QuestId = questId,
|
||||
QuestId = new QuestId(questId),
|
||||
Root = questRoot,
|
||||
Info = _questData.GetQuestInfo(questId),
|
||||
Info = _questData.GetQuestInfo(new QuestId(questId)),
|
||||
ReadOnly = true,
|
||||
};
|
||||
_quests[questId] = quest;
|
||||
_quests[quest.QuestId] = quest;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Loaded {Count} quests from assembly", _quests.Count);
|
||||
@ -136,21 +136,21 @@ internal sealed class QuestRegistry
|
||||
private void LoadQuestFromStream(string fileName, Stream stream)
|
||||
{
|
||||
_logger.LogTrace("Loading quest from '{FileName}'", fileName);
|
||||
ushort? questId = ExtractQuestIdFromName(fileName);
|
||||
IId? questId = ExtractQuestIdFromName(fileName);
|
||||
if (questId == null)
|
||||
return;
|
||||
|
||||
var questNode = JsonNode.Parse(stream)!;
|
||||
_jsonSchemaValidator.Enqueue(questId.Value, questNode);
|
||||
_jsonSchemaValidator.Enqueue(questId, questNode);
|
||||
|
||||
Quest quest = new Quest
|
||||
{
|
||||
QuestId = questId.Value,
|
||||
QuestId = questId,
|
||||
Root = questNode.Deserialize<QuestRoot>()!,
|
||||
Info = _questData.GetQuestInfo(questId.Value),
|
||||
Info = _questData.GetQuestInfo(questId),
|
||||
ReadOnly = false,
|
||||
};
|
||||
_quests[questId.Value] = quest;
|
||||
_quests[quest.QuestId] = quest;
|
||||
}
|
||||
|
||||
private void LoadFromDirectory(DirectoryInfo directory, LogLevel logLevel = LogLevel.Information)
|
||||
@ -179,7 +179,7 @@ internal sealed class QuestRegistry
|
||||
LoadFromDirectory(childDirectory, logLevel);
|
||||
}
|
||||
|
||||
private static ushort? ExtractQuestIdFromName(string resourceName)
|
||||
private static IId? ExtractQuestIdFromName(string resourceName)
|
||||
{
|
||||
string name = resourceName.Substring(0, resourceName.Length - ".json".Length);
|
||||
name = name.Substring(name.LastIndexOf('.') + 1);
|
||||
@ -188,11 +188,30 @@ internal sealed class QuestRegistry
|
||||
return null;
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -18,20 +18,20 @@ internal static class NextQuest
|
||||
if (step.NextQuestId == null)
|
||||
return null;
|
||||
|
||||
if (step.NextQuestId.Value == quest.QuestId)
|
||||
if (step.NextQuestId == quest.QuestId)
|
||||
return null;
|
||||
|
||||
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
|
||||
{
|
||||
public ushort NextQuestId { get; set; }
|
||||
public ushort CurrentQuestId { get; set; }
|
||||
public IId NextQuestId { get; set; } = null!;
|
||||
public IId CurrentQuestId { get; set; } = null!;
|
||||
|
||||
public ITask With(ushort nextQuestId, ushort currentQuestId)
|
||||
public ITask With(IId nextQuestId, IId currentQuestId)
|
||||
{
|
||||
NextQuestId = nextQuestId;
|
||||
CurrentQuestId = currentQuestId;
|
||||
|
@ -1,7 +1,9 @@
|
||||
namespace Questionable.Controller.Steps;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller.Steps;
|
||||
|
||||
internal interface ILastTask : ITask
|
||||
{
|
||||
public ushort QuestId { get; }
|
||||
public IId QuestId { get; }
|
||||
public int Sequence { get; }
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ internal static class Combat
|
||||
private CombatController.CombatData _combatData = 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)
|
||||
{
|
||||
_isLastStep = isLastStep;
|
||||
@ -107,9 +107,9 @@ internal static class Combat
|
||||
return ETaskResult.StillRunning;
|
||||
|
||||
// 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)
|
||||
return ETaskResult.StillRunning;
|
||||
|
||||
|
@ -118,7 +118,7 @@ internal static class UseItem
|
||||
private DateTime _continueAt;
|
||||
private int _itemCount;
|
||||
|
||||
public ushort? QuestId { get; set; }
|
||||
public IId? QuestId { get; set; }
|
||||
public uint ItemId { get; set; }
|
||||
public IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; set; } = new List<QuestWorkValue?>();
|
||||
public bool StartingCombat { get; set; }
|
||||
@ -142,9 +142,9 @@ internal static class UseItem
|
||||
|
||||
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 &&
|
||||
QuestWorkUtils.MatchesQuestWork(CompletionQuestVariablesFlags, questWork.Value))
|
||||
return ETaskResult.TaskComplete;
|
||||
@ -203,7 +203,7 @@ internal static class UseItem
|
||||
|
||||
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;
|
||||
DataId = dataId;
|
||||
@ -227,7 +227,7 @@ internal static class UseItem
|
||||
|
||||
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;
|
||||
Position = position;
|
||||
@ -249,7 +249,7 @@ internal static class UseItem
|
||||
|
||||
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)
|
||||
{
|
||||
QuestId = questId;
|
||||
@ -270,7 +270,7 @@ internal static class UseItem
|
||||
{
|
||||
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;
|
||||
ItemId = itemId;
|
||||
|
@ -45,9 +45,9 @@ internal static class SkipCondition
|
||||
{
|
||||
public QuestStep Step { 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;
|
||||
SkipConditions = skipConditions;
|
||||
@ -156,7 +156,9 @@ internal static class SkipCondition
|
||||
return true;
|
||||
}
|
||||
|
||||
QuestWork? questWork = gameFunctions.GetQuestEx(QuestId);
|
||||
if (QuestId is QuestId questId)
|
||||
{
|
||||
QuestWork? questWork = gameFunctions.GetQuestEx(questId);
|
||||
if (QuestWorkUtils.HasCompletionFlags(Step.CompletionQuestVariablesFlags) && questWork != null)
|
||||
{
|
||||
if (QuestWorkUtils.MatchesQuestWork(Step.CompletionQuestVariablesFlags, questWork.Value))
|
||||
@ -184,6 +186,7 @@ internal static class SkipCondition
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SkipConditions.ExtraCondition == EExtraSkipCondition.WakingSandsMainArea)
|
||||
{
|
||||
@ -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");
|
||||
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");
|
||||
return true;
|
||||
|
@ -30,7 +30,7 @@ internal static class WaitAtEnd
|
||||
if (step.CompletionQuestVariablesFlags.Count == 6 && QuestWorkUtils.HasCompletionFlags(step.CompletionQuestVariablesFlags))
|
||||
{
|
||||
var task = serviceProvider.GetRequiredService<WaitForCompletionFlags>()
|
||||
.With(quest, step);
|
||||
.With((QuestId)quest.QuestId, step);
|
||||
var delay = serviceProvider.GetRequiredService<WaitDelay>();
|
||||
return [task, delay, Next(quest, sequence)];
|
||||
}
|
||||
@ -162,11 +162,11 @@ internal static class WaitAtEnd
|
||||
|
||||
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 IList<QuestWorkValue?> Flags { get; set; } = null!;
|
||||
|
||||
public ITask With(Quest quest, QuestStep step)
|
||||
public ITask With(QuestId quest, QuestStep step)
|
||||
{
|
||||
Quest = quest;
|
||||
Step = step;
|
||||
@ -178,7 +178,7 @@ internal static class WaitAtEnd
|
||||
|
||||
public ETaskResult Update()
|
||||
{
|
||||
QuestWork? questWork = gameFunctions.GetQuestEx(Quest.QuestId);
|
||||
QuestWork? questWork = gameFunctions.GetQuestEx(Quest);
|
||||
return questWork != null &&
|
||||
QuestWorkUtils.MatchesQuestWork(Step.CompletionQuestVariablesFlags, questWork.Value)
|
||||
? ETaskResult.TaskComplete
|
||||
@ -214,11 +214,11 @@ internal static class WaitAtEnd
|
||||
$"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;
|
||||
return this;
|
||||
@ -228,23 +228,19 @@ internal static class WaitAtEnd
|
||||
|
||||
public ETaskResult Update()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var questManager = QuestManager.Instance();
|
||||
return questManager != null && questManager->IsQuestAccepted(QuestId)
|
||||
return gameFunctions.IsQuestAccepted(QuestId)
|
||||
? ETaskResult.TaskComplete
|
||||
: ETaskResult.StillRunning;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
return this;
|
||||
@ -254,15 +250,15 @@ internal static class WaitAtEnd
|
||||
|
||||
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})";
|
||||
}
|
||||
|
||||
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 bool Start() => true;
|
||||
@ -274,7 +270,7 @@ internal static class WaitAtEnd
|
||||
|
||||
internal sealed class EndAutomation : ILastTask
|
||||
{
|
||||
public ushort QuestId => throw new InvalidOperationException();
|
||||
public IId QuestId => throw new InvalidOperationException();
|
||||
public int Sequence => throw new InvalidOperationException();
|
||||
|
||||
public bool Start() => true;
|
||||
|
@ -3,6 +3,7 @@ using System.Linq;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Data;
|
||||
|
||||
@ -21,15 +22,17 @@ internal sealed class JournalData
|
||||
var genreLimsa = new Genre(uint.MaxValue - 3, "Starting in Limsa Lominsa", 1,
|
||||
new uint[] { 108, 109 }.Concat(limsaStart.Quest.Select(x => x.Row))
|
||||
.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,
|
||||
new uint[] { 85, 123, 124 }.Concat(gridaniaStart.Quest.Select(x => x.Row))
|
||||
.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,
|
||||
new uint[] { 568, 569, 570 }.Concat(uldahStart.Quest.Select(x => x.Row))
|
||||
.Where(x => x != 0)
|
||||
.Select(x => questData.GetQuestInfo((ushort)(x & 0xFFFF)))
|
||||
.Select(x => questData.GetQuestInfo(new QuestId((ushort)(x & 0xFFFF))))
|
||||
.ToList());
|
||||
genres.InsertRange(0, [genreLimsa, genreGridania, genreUldah]);
|
||||
genres.Single(x => x.Id == 1)
|
||||
|
@ -4,13 +4,14 @@ using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
using Quest = Lumina.Excel.GeneratedSheets.Quest;
|
||||
|
||||
namespace Questionable.Data;
|
||||
|
||||
internal sealed class QuestData
|
||||
{
|
||||
private readonly ImmutableDictionary<ushort, QuestInfo> _quests;
|
||||
private readonly ImmutableDictionary<QuestId, QuestInfo> _quests;
|
||||
|
||||
public QuestData(IDataManager dataManager)
|
||||
{
|
||||
@ -22,7 +23,15 @@ internal sealed class QuestData
|
||||
.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));
|
||||
}
|
||||
|
@ -89,37 +89,36 @@ internal sealed unsafe class GameFunctions
|
||||
|
||||
public DateTime ReturnRequestedAt { get; set; } = DateTime.MinValue;
|
||||
|
||||
public (ushort CurrentQuest, byte Sequence) GetCurrentQuest()
|
||||
public (IId? CurrentQuest, byte Sequence) GetCurrentQuest()
|
||||
{
|
||||
var (currentQuest, sequence) = GetCurrentQuestInternal();
|
||||
QuestManager* questManager = QuestManager.Instance();
|
||||
PlayerState* playerState = PlayerState.Instance();
|
||||
|
||||
if (currentQuest == 0)
|
||||
if (currentQuest == null || currentQuest.Value == 0)
|
||||
{
|
||||
if (_clientState.TerritoryType == 181) // Starting in Limsa
|
||||
return (107, 0);
|
||||
return (new QuestId(107), 0);
|
||||
if (_clientState.TerritoryType == 182) // Starting in Ul'dah
|
||||
return (594, 0);
|
||||
return (new QuestId(594), 0);
|
||||
if (_clientState.TerritoryType == 183) // Starting in Gridania
|
||||
return (39, 0);
|
||||
return (new QuestId(39), 0);
|
||||
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 (questManager->IsQuestAccepted(currentQuest) || QuestManager.IsQuestComplete(currentQuest))
|
||||
if (IsQuestAccepted(currentQuest) || IsQuestComplete(currentQuest))
|
||||
return (currentQuest, sequence);
|
||||
|
||||
// The company you keep...
|
||||
return _configuration.General.GrandCompany switch
|
||||
{
|
||||
GrandCompany.TwinAdder => (680, 0),
|
||||
GrandCompany.Maelstrom => (681, 0),
|
||||
GrandCompany.TwinAdder => (new QuestId(680), 0),
|
||||
GrandCompany.Maelstrom => (new QuestId(681), 0),
|
||||
_ => 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
|
||||
{
|
||||
@ -129,20 +128,20 @@ internal sealed unsafe class GameFunctions
|
||||
};
|
||||
|
||||
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
|
||||
ushort broadeningHorizons = 802;
|
||||
if (questManager->IsQuestAccepted(broadeningHorizons))
|
||||
return (broadeningHorizons, QuestManager.GetQuestSequence(broadeningHorizons));
|
||||
QuestId broadeningHorizons = new QuestId(802);
|
||||
if (IsQuestAccepted(broadeningHorizons))
|
||||
return (broadeningHorizons, QuestManager.GetQuestSequence(broadeningHorizons.Value));
|
||||
}
|
||||
|
||||
return (currentQuest, sequence);
|
||||
}
|
||||
|
||||
public (ushort CurrentQuest, byte Sequence) GetCurrentQuestInternal()
|
||||
public (IId? CurrentQuest, byte Sequence) GetCurrentQuestInternal()
|
||||
{
|
||||
var questManager = QuestManager.Instance();
|
||||
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
|
||||
// side quests until the end of time.
|
||||
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;
|
||||
|
||||
// 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.
|
||||
for (int i = questManager->TrackedQuests.Length - 1; i >= 0; --i)
|
||||
{
|
||||
ushort currentQuest;
|
||||
IId currentQuest;
|
||||
var trackedQuest = questManager->TrackedQuests[i];
|
||||
switch (trackedQuest.QuestType)
|
||||
{
|
||||
@ -167,12 +166,12 @@ internal sealed unsafe class GameFunctions
|
||||
continue;
|
||||
|
||||
case 1: // normal quest
|
||||
currentQuest = questManager->NormalQuests[trackedQuest.Index].QuestId;
|
||||
currentQuest = new QuestId(questManager->NormalQuests[trackedQuest.Index].QuestId);
|
||||
break;
|
||||
}
|
||||
|
||||
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
|
||||
@ -182,7 +181,7 @@ internal sealed unsafe class GameFunctions
|
||||
return default;
|
||||
}
|
||||
|
||||
private (ushort CurrentQuest, byte Sequence) GetMainScenarioQuest(QuestManager* questManager)
|
||||
private (QuestId? CurrentQuest, byte Sequence) GetMainScenarioQuest(QuestManager* questManager)
|
||||
{
|
||||
if (QuestManager.IsQuestComplete(3759)) // Memories Rekindled
|
||||
{
|
||||
@ -202,7 +201,7 @@ internal sealed unsafe class GameFunctions
|
||||
// redoHud+44 is chapter
|
||||
// redoHud+46 is quest
|
||||
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)
|
||||
return default;
|
||||
|
||||
ushort currentQuest = scenarioTree->Data->CurrentScenarioQuest;
|
||||
if (currentQuest == 0)
|
||||
QuestId currentQuest = new QuestId(scenarioTree->Data->CurrentScenarioQuest);
|
||||
if (currentQuest.Value == 0)
|
||||
return default;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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)
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
if (quest is { Info.IsRepeatable: true })
|
||||
@ -268,29 +274,50 @@ internal sealed unsafe class GameFunctions
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsQuestAcceptedOrComplete(ushort questId)
|
||||
public bool IsQuestAcceptedOrComplete(IId 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();
|
||||
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")]
|
||||
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);
|
||||
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)
|
||||
return true;
|
||||
else if (questInfo.QuestLockJoin == QuestInfo.QuestJoin.AtLeastOne && completedQuests > 0)
|
||||
@ -303,12 +330,12 @@ internal sealed unsafe class GameFunctions
|
||||
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)
|
||||
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 &&
|
||||
questInfo.PreviousQuests.Count == completedQuests)
|
||||
return true;
|
||||
@ -388,7 +415,7 @@ internal sealed unsafe class GameFunctions
|
||||
if (_configuration.Advanced.NeverFly)
|
||||
return false;
|
||||
|
||||
if (IsQuestAccepted(3304) && _condition[ConditionFlag.Mounted])
|
||||
if (IsQuestAccepted(new QuestId(3304)) && _condition[ConditionFlag.Mounted])
|
||||
{
|
||||
BattleChara* battleChara = (BattleChara*)(_clientState.LocalPlayer?.Address ?? 0);
|
||||
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)
|
||||
{
|
||||
var questRow =
|
||||
_dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets2.Quest>()!.GetRow((uint)currentQuest.QuestId +
|
||||
_dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets2.Quest>()!.GetRow((uint)currentQuest.QuestId.Value +
|
||||
0x10000);
|
||||
if (questRow == null)
|
||||
{
|
||||
@ -688,7 +715,7 @@ internal sealed unsafe class GameFunctions
|
||||
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);
|
||||
|
@ -6,7 +6,7 @@ namespace Questionable.Model;
|
||||
|
||||
internal sealed class Quest
|
||||
{
|
||||
public required ushort QuestId { get; init; }
|
||||
public required IId QuestId { get; init; }
|
||||
public required QuestRoot Root { get; init; }
|
||||
public required QuestInfo Info { get; init; }
|
||||
public required bool ReadOnly { get; init; }
|
||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
||||
using Dalamud.Game.Text;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using JetBrains.Annotations;
|
||||
using Questionable.Model.Questing;
|
||||
using ExcelQuest = Lumina.Excel.GeneratedSheets.Quest;
|
||||
|
||||
namespace Questionable.Model;
|
||||
@ -13,9 +14,9 @@ internal sealed class QuestInfo
|
||||
{
|
||||
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)",
|
||||
108 => " (Marauder)",
|
||||
@ -34,9 +35,15 @@ internal sealed class QuestInfo
|
||||
Level = quest.ClassJobLevel0;
|
||||
IssuerDataId = quest.IssuerStart;
|
||||
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;
|
||||
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;
|
||||
JournalGenre = quest.JournalGenre?.Row;
|
||||
SortKey = quest.SortKey;
|
||||
@ -49,14 +56,14 @@ internal sealed class QuestInfo
|
||||
}
|
||||
|
||||
|
||||
public ushort QuestId { get; }
|
||||
public QuestId QuestId { get; }
|
||||
public string Name { get; }
|
||||
public ushort Level { get; }
|
||||
public uint IssuerDataId { get; }
|
||||
public bool IsRepeatable { get; }
|
||||
public ImmutableList<ushort> PreviousQuests { get; }
|
||||
public ImmutableList<QuestId> PreviousQuests { get; }
|
||||
public QuestJoin PreviousQuestJoin { get; }
|
||||
public ImmutableList<ushort> QuestLocks { get; }
|
||||
public ImmutableList<QuestId> QuestLocks { get; }
|
||||
public QuestJoin QuestLockJoin { get; }
|
||||
public List<ushort> PreviousInstanceContent { get; }
|
||||
public QuestJoin PreviousInstanceContentJoin { get; }
|
||||
|
@ -1,10 +1,11 @@
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation;
|
||||
|
||||
internal sealed record ValidationIssue
|
||||
{
|
||||
public required ushort? QuestId { get; init; }
|
||||
public required IId? QuestId { get; init; }
|
||||
public required byte? Sequence { get; init; }
|
||||
public required int? Step { get; init; }
|
||||
public EBeastTribe BeastTribe { get; init; } = EBeastTribe.None;
|
||||
|
@ -23,7 +23,7 @@ internal sealed class AethernetShortcutValidator : IQuestValidator
|
||||
.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)
|
||||
return null;
|
||||
|
@ -4,13 +4,14 @@ using System.Globalization;
|
||||
using System.Text.Json.Nodes;
|
||||
using Json.Schema;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
using Questionable.QuestPaths;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class JsonSchemaValidator : IQuestValidator
|
||||
{
|
||||
private readonly Dictionary<ushort, JsonNode> _questNodes = new();
|
||||
private readonly Dictionary<IId, JsonNode> _questNodes = new();
|
||||
private JsonSchema? _questSchema;
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ internal sealed class DebugOverlay : Window
|
||||
IsOpen = true;
|
||||
}
|
||||
|
||||
public ushort? HighlightedQuest { get; set; }
|
||||
public IId? HighlightedQuest { get; set; }
|
||||
|
||||
public override bool DrawConditions() => _configuration.Advanced.DebugOverlay;
|
||||
|
||||
@ -93,7 +93,7 @@ internal sealed class DebugOverlay : Window
|
||||
|
||||
private void DrawHighlightedQuest()
|
||||
{
|
||||
if (HighlightedQuest == null || !_questRegistry.TryGetQuest(HighlightedQuest.Value, out var quest))
|
||||
if (HighlightedQuest == null || !_questRegistry.TryGetQuest(HighlightedQuest, out var quest))
|
||||
return;
|
||||
|
||||
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))
|
||||
{
|
||||
_commandManager.DispatchCommand("/questinfo",
|
||||
questInfo.QuestId.ToString(CultureInfo.InvariantCulture), commandInfo);
|
||||
_commandManager.DispatchCommand("/questinfo", questInfo.QuestId.ToString(), commandInfo);
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
|
@ -4,16 +4,19 @@ using Dalamud.Interface.Utility.Raii;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||
using FFXIVClientStructs.FFXIV.Common.Math;
|
||||
using Questionable.Data;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Windows.QuestComponents;
|
||||
|
||||
internal sealed class ARealmRebornComponent
|
||||
{
|
||||
private const ushort ATimeForEveryPurpose = 425;
|
||||
private const ushort TheUltimateWeapon = 524;
|
||||
private const ushort GoodIntentions = 363;
|
||||
private static readonly QuestId ATimeForEveryPurpose = new(425);
|
||||
private static readonly QuestId TheUltimateWeapon = new(524);
|
||||
private static readonly QuestId GoodIntentions = new(363);
|
||||
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 QuestData _questData;
|
||||
|
@ -151,7 +151,10 @@ internal sealed class ActiveQuestComponent
|
||||
|
||||
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)
|
||||
{
|
||||
Vector4 color;
|
||||
@ -271,7 +274,7 @@ internal sealed class ActiveQuestComponent
|
||||
ImGui.SameLine();
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Atlas))
|
||||
_commandManager.DispatchCommand("/questinfo",
|
||||
currentQuest.Quest.QuestId.ToString(CultureInfo.InvariantCulture), commandInfo);
|
||||
currentQuest.Quest.QuestId.ToString() ?? string.Empty, commandInfo);
|
||||
}
|
||||
|
||||
bool autoAcceptNextQuest = _configuration.General.AutoAcceptNextQuest;
|
||||
|
@ -110,7 +110,7 @@ internal sealed class QuestSelectionWindow : LWindow
|
||||
|
||||
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))
|
||||
_quests.Add(_questData.GetQuestInfo(questId));
|
||||
}
|
||||
@ -161,7 +161,7 @@ internal sealed class QuestSelectionWindow : LWindow
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
|
||||
string questId = quest.QuestId.ToString(CultureInfo.InvariantCulture);
|
||||
string questId = quest.QuestId.ToString();
|
||||
bool isKnownQuest = _questRegistry.TryGetQuest(quest.QuestId, out var knownQuest);
|
||||
|
||||
if (ImGui.TableNextColumn())
|
||||
|
@ -56,11 +56,11 @@ internal sealed class QuestValidationWindow : LWindow
|
||||
ImGui.TableNextRow();
|
||||
|
||||
if (ImGui.TableNextColumn())
|
||||
ImGui.TextUnformatted(validationIssue.QuestId?.ToString(CultureInfo.InvariantCulture) ?? string.Empty);
|
||||
ImGui.TextUnformatted(validationIssue.QuestId?.ToString() ?? string.Empty);
|
||||
|
||||
if (ImGui.TableNextColumn())
|
||||
ImGui.TextUnformatted(validationIssue.QuestId != null
|
||||
? _questData.GetQuestInfo(validationIssue.QuestId.Value).Name
|
||||
? _questData.GetQuestInfo(validationIssue.QuestId).Name
|
||||
: validationIssue.BeastTribe.ToString());
|
||||
|
||||
if (ImGui.TableNextColumn())
|
||||
|
@ -4,6 +4,7 @@ using Dalamud.Interface.Colors;
|
||||
using Dalamud.Plugin;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||
using ImGuiNET;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Windows;
|
||||
|
||||
@ -18,7 +19,7 @@ internal sealed class UiUtils
|
||||
_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))
|
||||
return (ImGuiColors.DalamudYellow, FontAwesomeIcon.Running, "Active");
|
||||
|
Loading…
Reference in New Issue
Block a user