Validation: Sum up disabled tribe quests instead of showing each one individually

pull/14/head v1.19
Liza 2024-07-27 01:51:21 +02:00
parent ec3a0eb82f
commit 3483d07ff2
Signed by: liza
GPG Key ID: 7199F8D727D55F67
13 changed files with 100 additions and 7 deletions

View File

@ -0,0 +1,28 @@
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;
namespace Questionable.Model;
[SuppressMessage("Design", "CA1028", Justification = "Game type")]
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public enum EBeastTribe : byte
{
None = 0,
Amaljaa = 1,
Sylphs = 2,
Kobolds = 3,
Sahagin = 4,
Ixal = 5,
VanuVanu = 6,
Vath = 7,
Moogles = 8,
Kojin = 9,
Ananta = 10,
Namazu = 11,
Pixies = 12,
Qitari = 13,
Dwarves = 14,
Arkasodara = 15,
Omicrons = 16,
Loporrits = 17,
}

View File

@ -27,6 +27,7 @@ internal sealed class QuestInfo
PreviousInstanceContent = quest.InstanceContent.Select(x => (ushort)x.Row).Where(x => x != 0).ToList(); PreviousInstanceContent = quest.InstanceContent.Select(x => (ushort)x.Row).Where(x => x != 0).ToList();
PreviousInstanceContentJoin = (QuestJoin)quest.InstanceContentJoin; PreviousInstanceContentJoin = (QuestJoin)quest.InstanceContentJoin;
GrandCompany = (GrandCompany)quest.GrandCompany.Row; GrandCompany = (GrandCompany)quest.GrandCompany.Row;
BeastTribe = (EBeastTribe)quest.BeastTribe.Row;
} }
@ -44,6 +45,7 @@ internal sealed class QuestInfo
public bool IsMainScenarioQuest { get; } public bool IsMainScenarioQuest { get; }
public bool CompletesInstantly { get; } public bool CompletesInstantly { get; }
public GrandCompany GrandCompany { get; } public GrandCompany GrandCompany { get; }
public EBeastTribe BeastTribe { get; }
public string SimplifiedName => Name public string SimplifiedName => Name
.TrimStart(SeIconChar.QuestSync.ToIconChar(), SeIconChar.QuestRepeatable.ToIconChar(), ' '); .TrimStart(SeIconChar.QuestSync.ToIconChar(), SeIconChar.QuestRepeatable.ToIconChar(), ' ');

View File

@ -1,6 +1,6 @@
<Project Sdk="Dalamud.NET.Sdk/9.0.2"> <Project Sdk="Dalamud.NET.Sdk/9.0.2">
<PropertyGroup> <PropertyGroup>
<Version>1.18</Version> <Version>1.19</Version>
<OutputPath>dist</OutputPath> <OutputPath>dist</OutputPath>
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap> <PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>

View File

@ -0,0 +1,18 @@
namespace Questionable.Validation;
public enum EIssueType
{
None,
InvalidJsonSchema,
MissingSequence0,
MissingSequence,
DuplicateSequence,
MissingQuestAccept,
MissingQuestComplete,
InstantQuestWithMultipleSteps,
DuplicateCompletionFlags,
InvalidNextQuestId,
QuestDisabled,
UnexpectedAcceptQuestStep,
UnexpectedCompleteQuestStep,
}

View File

@ -41,6 +41,7 @@ internal sealed class QuestValidator
{ {
try try
{ {
Dictionary<EBeastTribe, int> disabledTribeQuests = new();
foreach (var quest in quests) foreach (var quest in quests)
{ {
foreach (var validator in _validators) foreach (var validator in _validators)
@ -53,7 +54,13 @@ internal sealed class QuestValidator
_logger.Log(level, _logger.Log(level,
"Validation failed: {QuestId} ({QuestName}) / {QuestSequence} / {QuestStep} - {Description}", "Validation failed: {QuestId} ({QuestName}) / {QuestSequence} / {QuestStep} - {Description}",
issue.QuestId, quest.Info.Name, issue.Sequence, issue.Step, issue.Description); issue.QuestId, quest.Info.Name, issue.Sequence, issue.Step, issue.Description);
_validationIssues.Add(issue); if (issue.Type == EIssueType.QuestDisabled && quest.Info.BeastTribe != EBeastTribe.None)
{
disabledTribeQuests.TryAdd(quest.Info.BeastTribe, 0);
disabledTribeQuests[quest.Info.BeastTribe]++;
}
else
_validationIssues.Add(issue);
} }
} }
} }
@ -62,6 +69,7 @@ internal sealed class QuestValidator
.ThenBy(x => x.Sequence) .ThenBy(x => x.Sequence)
.ThenBy(x => x.Step) .ThenBy(x => x.Step)
.ThenBy(x => x.Description) .ThenBy(x => x.Description)
.Concat(DisabledTribesAsIssues(disabledTribeQuests))
.ToList(); .ToList();
} }
catch (Exception e) catch (Exception e)
@ -70,4 +78,20 @@ internal sealed class QuestValidator
} }
}, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
} }
private static IEnumerable<ValidationIssue> DisabledTribesAsIssues(Dictionary<EBeastTribe, int> disabledTribeQuests)
{
return disabledTribeQuests
.OrderBy(x => x.Key)
.Select(x => new ValidationIssue
{
QuestId = null,
Sequence = null,
Step = null,
BeastTribe = x.Key,
Type = EIssueType.QuestDisabled,
Severity = EIssueSeverity.None,
Description = $"{x.Value} disabled quest(s)",
});
}
} }

View File

@ -1,10 +1,14 @@
namespace Questionable.Validation; using Questionable.Model;
namespace Questionable.Validation;
internal sealed record ValidationIssue internal sealed record ValidationIssue
{ {
public required ushort QuestId { get; init; } public required ushort? 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 required EIssueType Type { get; init; }
public required EIssueSeverity Severity { get; init; } public required EIssueSeverity Severity { get; init; }
public required string Description { get; init; } public required string Description { get; init; }
} }

View File

@ -21,6 +21,7 @@ internal sealed class BasicSequenceValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = 0, Sequence = 0,
Step = null, Step = null,
Type = EIssueType.MissingSequence0,
Severity = EIssueSeverity.Error, Severity = EIssueSeverity.Error,
Description = "Missing quest start", Description = "Missing quest start",
}; };
@ -39,6 +40,7 @@ internal sealed class BasicSequenceValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = (byte)sequence.Sequence, Sequence = (byte)sequence.Sequence,
Step = null, Step = null,
Type = EIssueType.InstantQuestWithMultipleSteps,
Severity = EIssueSeverity.Error, Severity = EIssueSeverity.Error,
Description = "Instant quest should not have any sequences after the start", Description = "Instant quest should not have any sequences after the start",
}; };
@ -74,6 +76,7 @@ internal sealed class BasicSequenceValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = (byte)sequenceNo, Sequence = (byte)sequenceNo,
Step = null, Step = null,
Type = EIssueType.MissingSequence,
Severity = EIssueSeverity.Error, Severity = EIssueSeverity.Error,
Description = "Missing sequence", Description = "Missing sequence",
}; };
@ -85,6 +88,7 @@ internal sealed class BasicSequenceValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = (byte)sequenceNo, Sequence = (byte)sequenceNo,
Step = null, Step = null,
Type = EIssueType.DuplicateSequence,
Severity = EIssueSeverity.Error, Severity = EIssueSeverity.Error,
Description = "Duplicate sequence", Description = "Duplicate sequence",
}; };

View File

@ -44,6 +44,7 @@ internal sealed class CompletionFlagsValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = (byte)sequence.Sequence, Sequence = (byte)sequence.Sequence,
Step = i, Step = i,
Type = EIssueType.DuplicateCompletionFlags,
Severity = EIssueSeverity.Error, Severity = EIssueSeverity.Error,
Description = $"Duplicate completion flags: {string.Join(", ", sequence.Steps[i].CompletionQuestVariablesFlags)}", Description = $"Duplicate completion flags: {string.Join(", ", sequence.Steps[i].CompletionQuestVariablesFlags)}",
}; };

View File

@ -30,6 +30,7 @@ internal sealed class JsonSchemaValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = null, Sequence = null,
Step = null, Step = null,
Type = EIssueType.InvalidJsonSchema,
Severity = EIssueSeverity.Error, Severity = EIssueSeverity.Error,
Description = "JSON Validation failed" Description = "JSON Validation failed"
}; };

View File

@ -15,6 +15,7 @@ internal sealed class NextQuestValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = (byte)invalidNextQuest.Sequence.Sequence, Sequence = (byte)invalidNextQuest.Sequence.Sequence,
Step = invalidNextQuest.StepId, Step = invalidNextQuest.StepId,
Type = EIssueType.InvalidNextQuestId,
Severity = EIssueSeverity.Error, Severity = EIssueSeverity.Error,
Description = "Next quest should not reference itself", Description = "Next quest should not reference itself",
}; };

View File

@ -14,6 +14,7 @@ internal sealed class QuestDisabledValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = null, Sequence = null,
Step = null, Step = null,
Type = EIssueType.QuestDisabled,
Severity = EIssueSeverity.None, Severity = EIssueSeverity.None,
Description = "Quest is disabled", Description = "Quest is disabled",
}; };

View File

@ -21,6 +21,7 @@ internal sealed class UniqueStartStopValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = (byte)accept.Sequence.Sequence, Sequence = (byte)accept.Sequence.Sequence,
Step = accept.StepId, Step = accept.StepId,
Type = EIssueType.UnexpectedAcceptQuestStep,
Severity = EIssueSeverity.Error, Severity = EIssueSeverity.Error,
Description = "Unexpected AcceptQuest step", Description = "Unexpected AcceptQuest step",
}; };
@ -34,6 +35,7 @@ internal sealed class UniqueStartStopValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = 0, Sequence = 0,
Step = null, Step = null,
Type = EIssueType.MissingQuestAccept,
Severity = EIssueSeverity.Error, Severity = EIssueSeverity.Error,
Description = "No AcceptQuest step", Description = "No AcceptQuest step",
}; };
@ -51,6 +53,7 @@ internal sealed class UniqueStartStopValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = (byte)complete.Sequence.Sequence, Sequence = (byte)complete.Sequence.Sequence,
Step = complete.StepId, Step = complete.StepId,
Type = EIssueType.UnexpectedCompleteQuestStep,
Severity = EIssueSeverity.Error, Severity = EIssueSeverity.Error,
Description = "Unexpected CompleteQuest step", Description = "Unexpected CompleteQuest step",
}; };
@ -64,6 +67,7 @@ internal sealed class UniqueStartStopValidator : IQuestValidator
QuestId = quest.QuestId, QuestId = quest.QuestId,
Sequence = 255, Sequence = 255,
Step = null, Step = null,
Type = EIssueType.MissingQuestComplete,
Severity = EIssueSeverity.Error, Severity = EIssueSeverity.Error,
Description = "No CompleteQuest step", Description = "No CompleteQuest step",
}; };

View File

@ -19,7 +19,9 @@ internal sealed class QuestValidationWindow : LWindow
private readonly QuestData _questData; private readonly QuestData _questData;
private readonly IDalamudPluginInterface _pluginInterface; private readonly IDalamudPluginInterface _pluginInterface;
public QuestValidationWindow(QuestValidator questValidator, QuestData questData, IDalamudPluginInterface pluginInterface) : base("Quest Validation###QuestionableValidator") public QuestValidationWindow(QuestValidator questValidator, QuestData questData,
IDalamudPluginInterface pluginInterface)
: base("Quest Validation###QuestionableValidator")
{ {
_questValidator = questValidator; _questValidator = questValidator;
_questData = questData; _questData = questData;
@ -54,10 +56,12 @@ internal sealed class QuestValidationWindow : LWindow
ImGui.TableNextRow(); ImGui.TableNextRow();
if (ImGui.TableNextColumn()) if (ImGui.TableNextColumn())
ImGui.TextUnformatted(validationIssue.QuestId.ToString(CultureInfo.InvariantCulture)); ImGui.TextUnformatted(validationIssue.QuestId?.ToString(CultureInfo.InvariantCulture) ?? string.Empty);
if (ImGui.TableNextColumn()) if (ImGui.TableNextColumn())
ImGui.TextUnformatted(_questData.GetQuestInfo(validationIssue.QuestId).Name); ImGui.TextUnformatted(validationIssue.QuestId != null
? _questData.GetQuestInfo(validationIssue.QuestId.Value).Name
: validationIssue.BeastTribe.ToString());
if (ImGui.TableNextColumn()) if (ImGui.TableNextColumn())
ImGui.TextUnformatted(validationIssue.Sequence?.ToString(CultureInfo.InvariantCulture) ?? string.Empty); ImGui.TextUnformatted(validationIssue.Sequence?.ToString(CultureInfo.InvariantCulture) ?? string.Empty);
@ -81,6 +85,7 @@ internal sealed class QuestValidationWindow : LWindow
ImGui.TextUnformatted(FontAwesomeIcon.InfoCircle.ToIconString()); ImGui.TextUnformatted(FontAwesomeIcon.InfoCircle.ToIconString());
} }
} }
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextUnformatted(validationIssue.Description); ImGui.TextUnformatted(validationIssue.Description);
} }