--- /dev/null
+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,
+}
PreviousInstanceContent = quest.InstanceContent.Select(x => (ushort)x.Row).Where(x => x != 0).ToList();
PreviousInstanceContentJoin = (QuestJoin)quest.InstanceContentJoin;
GrandCompany = (GrandCompany)quest.GrandCompany.Row;
+ BeastTribe = (EBeastTribe)quest.BeastTribe.Row;
}
public bool IsMainScenarioQuest { get; }
public bool CompletesInstantly { get; }
public GrandCompany GrandCompany { get; }
+ public EBeastTribe BeastTribe { get; }
public string SimplifiedName => Name
.TrimStart(SeIconChar.QuestSync.ToIconChar(), SeIconChar.QuestRepeatable.ToIconChar(), ' ');
<Project Sdk="Dalamud.NET.Sdk/9.0.2">
<PropertyGroup>
- <Version>1.18</Version>
+ <Version>1.19</Version>
<OutputPath>dist</OutputPath>
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
<Platforms>x64</Platforms>
--- /dev/null
+namespace Questionable.Validation;
+
+public enum EIssueType
+{
+ None,
+ InvalidJsonSchema,
+ MissingSequence0,
+ MissingSequence,
+ DuplicateSequence,
+ MissingQuestAccept,
+ MissingQuestComplete,
+ InstantQuestWithMultipleSteps,
+ DuplicateCompletionFlags,
+ InvalidNextQuestId,
+ QuestDisabled,
+ UnexpectedAcceptQuestStep,
+ UnexpectedCompleteQuestStep,
+}
{
try
{
+ Dictionary<EBeastTribe, int> disabledTribeQuests = new();
foreach (var quest in quests)
{
foreach (var validator in _validators)
_logger.Log(level,
"Validation failed: {QuestId} ({QuestName}) / {QuestSequence} / {QuestStep} - {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);
}
}
}
.ThenBy(x => x.Sequence)
.ThenBy(x => x.Step)
.ThenBy(x => x.Description)
+ .Concat(DisabledTribesAsIssues(disabledTribeQuests))
.ToList();
}
catch (Exception e)
}
}, 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)",
+ });
+ }
}
-namespace Questionable.Validation;
+using Questionable.Model;
+
+namespace Questionable.Validation;
internal sealed record ValidationIssue
{
- public required ushort QuestId { get; init; }
+ public required ushort? QuestId { get; init; }
public required byte? Sequence { 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 string Description { get; init; }
}
QuestId = quest.QuestId,
Sequence = 0,
Step = null,
+ Type = EIssueType.MissingSequence0,
Severity = EIssueSeverity.Error,
Description = "Missing quest start",
};
QuestId = quest.QuestId,
Sequence = (byte)sequence.Sequence,
Step = null,
+ Type = EIssueType.InstantQuestWithMultipleSteps,
Severity = EIssueSeverity.Error,
Description = "Instant quest should not have any sequences after the start",
};
QuestId = quest.QuestId,
Sequence = (byte)sequenceNo,
Step = null,
+ Type = EIssueType.MissingSequence,
Severity = EIssueSeverity.Error,
Description = "Missing sequence",
};
QuestId = quest.QuestId,
Sequence = (byte)sequenceNo,
Step = null,
+ Type = EIssueType.DuplicateSequence,
Severity = EIssueSeverity.Error,
Description = "Duplicate sequence",
};
QuestId = quest.QuestId,
Sequence = (byte)sequence.Sequence,
Step = i,
+ Type = EIssueType.DuplicateCompletionFlags,
Severity = EIssueSeverity.Error,
Description = $"Duplicate completion flags: {string.Join(", ", sequence.Steps[i].CompletionQuestVariablesFlags)}",
};
QuestId = quest.QuestId,
Sequence = null,
Step = null,
+ Type = EIssueType.InvalidJsonSchema,
Severity = EIssueSeverity.Error,
Description = "JSON Validation failed"
};
QuestId = quest.QuestId,
Sequence = (byte)invalidNextQuest.Sequence.Sequence,
Step = invalidNextQuest.StepId,
+ Type = EIssueType.InvalidNextQuestId,
Severity = EIssueSeverity.Error,
Description = "Next quest should not reference itself",
};
QuestId = quest.QuestId,
Sequence = null,
Step = null,
+ Type = EIssueType.QuestDisabled,
Severity = EIssueSeverity.None,
Description = "Quest is disabled",
};
QuestId = quest.QuestId,
Sequence = (byte)accept.Sequence.Sequence,
Step = accept.StepId,
+ Type = EIssueType.UnexpectedAcceptQuestStep,
Severity = EIssueSeverity.Error,
Description = "Unexpected AcceptQuest step",
};
QuestId = quest.QuestId,
Sequence = 0,
Step = null,
+ Type = EIssueType.MissingQuestAccept,
Severity = EIssueSeverity.Error,
Description = "No AcceptQuest step",
};
QuestId = quest.QuestId,
Sequence = (byte)complete.Sequence.Sequence,
Step = complete.StepId,
+ Type = EIssueType.UnexpectedCompleteQuestStep,
Severity = EIssueSeverity.Error,
Description = "Unexpected CompleteQuest step",
};
QuestId = quest.QuestId,
Sequence = 255,
Step = null,
+ Type = EIssueType.MissingQuestComplete,
Severity = EIssueSeverity.Error,
Description = "No CompleteQuest step",
};
private readonly QuestData _questData;
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;
_questData = questData;
ImGui.TableNextRow();
if (ImGui.TableNextColumn())
- ImGui.TextUnformatted(validationIssue.QuestId.ToString(CultureInfo.InvariantCulture));
+ ImGui.TextUnformatted(validationIssue.QuestId?.ToString(CultureInfo.InvariantCulture) ?? string.Empty);
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())
ImGui.TextUnformatted(validationIssue.Sequence?.ToString(CultureInfo.InvariantCulture) ?? string.Empty);
ImGui.TextUnformatted(FontAwesomeIcon.InfoCircle.ToIconString());
}
}
+
ImGui.SameLine();
ImGui.TextUnformatted(validationIssue.Description);
}