punish v6.8.18.0
This commit is contained in:
commit
cfb4dea47e
316 changed files with 554088 additions and 0 deletions
|
@ -0,0 +1,49 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Questionable.Data;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Common;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class AethernetShortcutValidator : IQuestValidator
|
||||
{
|
||||
private readonly AetheryteData _aetheryteData;
|
||||
|
||||
public AethernetShortcutValidator(AetheryteData aetheryteData)
|
||||
{
|
||||
_aetheryteData = aetheryteData;
|
||||
}
|
||||
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
return (from x in quest.AllSteps()
|
||||
select Validate(quest.Id, x.Sequence.Sequence, x.StepId, x.Step.AethernetShortcut) into x
|
||||
where x != null
|
||||
select x).Cast<ValidationIssue>();
|
||||
}
|
||||
|
||||
private ValidationIssue? Validate(ElementId elementId, int sequenceNo, int stepId, AethernetShortcut? aethernetShortcut)
|
||||
{
|
||||
if (aethernetShortcut == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ushort valueOrDefault = _aetheryteData.AethernetGroups.GetValueOrDefault(aethernetShortcut.From);
|
||||
ushort valueOrDefault2 = _aetheryteData.AethernetGroups.GetValueOrDefault(aethernetShortcut.To);
|
||||
if (valueOrDefault != valueOrDefault2)
|
||||
{
|
||||
return new ValidationIssue
|
||||
{
|
||||
ElementId = elementId,
|
||||
Sequence = (byte)sequenceNo,
|
||||
Step = stepId,
|
||||
Type = EIssueType.InvalidAethernetShortcut,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = $"Invalid aethernet shortcut: {aethernetShortcut.From} to {aethernetShortcut.To}"
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class BasicSequenceValidator : IQuestValidator
|
||||
{
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
List<QuestSequence> sequences = quest.Root.QuestSequence;
|
||||
QuestSequence foundStart = sequences.FirstOrDefault((QuestSequence x) => x.Sequence == 0);
|
||||
if (foundStart == null)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = 0,
|
||||
Step = null,
|
||||
Type = EIssueType.MissingSequence0,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Missing quest start"
|
||||
};
|
||||
}
|
||||
else if (quest.Info is QuestInfo { CompletesInstantly: not false })
|
||||
{
|
||||
foreach (QuestSequence item in sequences)
|
||||
{
|
||||
if (item != foundStart)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = item.Sequence,
|
||||
Step = null,
|
||||
Type = EIssueType.InstantQuestWithMultipleSteps,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Instant quest should not have any sequences after the start"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(quest.Info is QuestInfo))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
int maxSequence = (from x in sequences
|
||||
select x.Sequence into x
|
||||
where x != byte.MaxValue
|
||||
select x).Max();
|
||||
int i;
|
||||
for (i = 0; i < maxSequence; i++)
|
||||
{
|
||||
List<QuestSequence> foundSequences = sequences.Where((QuestSequence x) => x.Sequence == i).ToList();
|
||||
ValidationIssue validationIssue = ValidateSequences(quest, i, foundSequences);
|
||||
if (validationIssue != null)
|
||||
{
|
||||
yield return validationIssue;
|
||||
}
|
||||
}
|
||||
List<QuestSequence> foundSequences2 = sequences.Where((QuestSequence x) => x.Sequence == byte.MaxValue).ToList();
|
||||
ValidationIssue validationIssue2 = ValidateSequences(quest, 255, foundSequences2);
|
||||
if (validationIssue2 != null)
|
||||
{
|
||||
yield return validationIssue2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ValidationIssue? ValidateSequences(Quest quest, int sequenceNo, List<QuestSequence> foundSequences)
|
||||
{
|
||||
if (foundSequences.Count == 0)
|
||||
{
|
||||
return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = (byte)sequenceNo,
|
||||
Step = null,
|
||||
Type = EIssueType.MissingSequence,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Missing sequence"
|
||||
};
|
||||
}
|
||||
if (foundSequences.Count == 2)
|
||||
{
|
||||
return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = (byte)sequenceNo,
|
||||
Step = null,
|
||||
Type = EIssueType.DuplicateSequence,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Duplicate sequence"
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System.Collections.Generic;
|
||||
using LLib.GameData;
|
||||
using Questionable.Data;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class ClassQuestShouldHaveShortcutValidator : IQuestValidator
|
||||
{
|
||||
private readonly HashSet<ElementId> _classJobQuests = new HashSet<ElementId>();
|
||||
|
||||
public ClassQuestShouldHaveShortcutValidator(QuestData questData)
|
||||
{
|
||||
foreach (EClassJob enumValue in typeof(EClassJob).GetEnumValues())
|
||||
{
|
||||
if (enumValue == EClassJob.Adventurer)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (QuestInfo classJobQuest in questData.GetClassJobQuests(enumValue))
|
||||
{
|
||||
if (classJobQuest.Level > 1)
|
||||
{
|
||||
_classJobQuests.Add(classJobQuest.QuestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
if (_classJobQuests.Contains(quest.Id))
|
||||
{
|
||||
QuestStep questStep = quest.FindSequence(0)?.FindStep(0);
|
||||
if (questStep != null && !questStep.IsTeleportableForPriorityQuests())
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = 0,
|
||||
Step = 0,
|
||||
Type = EIssueType.ClassQuestWithoutAetheryteShortcut,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Class quest should have an aetheryte shortcut to be done automatically"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Questionable.Controller.Utils;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class CompletionFlagsValidator : IQuestValidator
|
||||
{
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
if (quest.Id.Value == 5149)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (QuestSequence sequence in quest.AllSequences())
|
||||
{
|
||||
List<long> mappedCompletionFlags = sequence.Steps.Select((QuestStep x) => QuestWorkUtils.HasCompletionFlags(x.CompletionQuestVariablesFlags) ? Enumerable.Range(0, 6).Select(delegate(int y)
|
||||
{
|
||||
QuestWorkValue questWorkValue = x.CompletionQuestVariablesFlags[y];
|
||||
return (long)((questWorkValue == null) ? 0 : BitOperations.RotateLeft((ulong)(questWorkValue.High.GetValueOrDefault() * 16 + questWorkValue.Low.GetValueOrDefault()), 8 * y));
|
||||
}).Sum() : 0).ToList();
|
||||
int i = 0;
|
||||
while (i < sequence.Steps.Count)
|
||||
{
|
||||
long flags = mappedCompletionFlags[i];
|
||||
if (flags != 0L && mappedCompletionFlags.Count((long x) => x == flags) >= 2)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = sequence.Sequence,
|
||||
Step = i,
|
||||
Type = EIssueType.DuplicateCompletionFlags,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Duplicate completion flags: " + string.Join(", ", sequence.Steps[i].CompletionQuestVariablesFlags)
|
||||
};
|
||||
}
|
||||
int num = i + 1;
|
||||
i = num;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Functions;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class DialogueChoiceValidator : IQuestValidator
|
||||
{
|
||||
private readonly ExcelFunctions _excelFunctions;
|
||||
|
||||
public DialogueChoiceValidator(ExcelFunctions excelFunctions)
|
||||
{
|
||||
_excelFunctions = excelFunctions;
|
||||
}
|
||||
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
foreach (var x in quest.AllSteps())
|
||||
{
|
||||
if (x.Step.DialogueChoices.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (DialogueChoice dialogueChoice in x.Step.DialogueChoices)
|
||||
{
|
||||
ExcelRef prompt = dialogueChoice.Prompt;
|
||||
if (prompt != null)
|
||||
{
|
||||
ValidationIssue validationIssue = Validate(quest, x.Sequence, x.StepId, dialogueChoice.ExcelSheet, prompt, "Prompt");
|
||||
if (validationIssue != null)
|
||||
{
|
||||
yield return validationIssue;
|
||||
}
|
||||
}
|
||||
ExcelRef answer = dialogueChoice.Answer;
|
||||
if (answer != null)
|
||||
{
|
||||
ValidationIssue validationIssue2 = Validate(quest, x.Sequence, x.StepId, dialogueChoice.ExcelSheet, answer, "Answer");
|
||||
if (validationIssue2 != null)
|
||||
{
|
||||
yield return validationIssue2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ValidationIssue? Validate(Quest quest, QuestSequence sequence, int stepId, string? excelSheet, ExcelRef excelRef, string label)
|
||||
{
|
||||
if (excelRef.Type == ExcelRef.EType.Key)
|
||||
{
|
||||
if (!_excelFunctions.GetRawDialogueText(quest, excelSheet, excelRef.AsKey()).HasValue)
|
||||
{
|
||||
return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = sequence.Sequence,
|
||||
Step = stepId,
|
||||
Type = EIssueType.InvalidExcelRef,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = $"{label} invalid: {excelSheet} → {excelRef.AsKey()}"
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (excelRef.Type == ExcelRef.EType.RowId && !_excelFunctions.GetRawDialogueTextByRowId(excelSheet, excelRef.AsRowId()).HasValue)
|
||||
{
|
||||
return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = sequence.Sequence,
|
||||
Step = stepId,
|
||||
Type = EIssueType.InvalidExcelRef,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = $"{label} invalid: {excelSheet} → {excelRef.AsRowId()}"
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
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<ElementId, JsonNode> _questNodes = new Dictionary<ElementId, JsonNode>();
|
||||
|
||||
private JsonSchema? _questSchema;
|
||||
|
||||
public JsonSchemaValidator()
|
||||
{
|
||||
SchemaRegistry.Global.Register(new Uri("https://qstxiv.github.io/schema/common-aethernetshard.json"), JsonSchema.FromStream(AssemblyModelLoader.CommonAethernetShard).AsTask().Result);
|
||||
SchemaRegistry.Global.Register(new Uri("https://qstxiv.github.io/schema/common-aetheryte.json"), JsonSchema.FromStream(AssemblyModelLoader.CommonAetheryte).AsTask().Result);
|
||||
SchemaRegistry.Global.Register(new Uri("https://qstxiv.github.io/schema/common-classjob.json"), JsonSchema.FromStream(AssemblyModelLoader.CommonClassJob).AsTask().Result);
|
||||
SchemaRegistry.Global.Register(new Uri("https://qstxiv.github.io/schema/common-completionflags.json"), JsonSchema.FromStream(AssemblyModelLoader.CommonCompletionFlags).AsTask().Result);
|
||||
SchemaRegistry.Global.Register(new Uri("https://qstxiv.github.io/schema/common-vector3.json"), JsonSchema.FromStream(AssemblyModelLoader.CommonVector3).AsTask().Result);
|
||||
}
|
||||
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
if (_questSchema == null)
|
||||
{
|
||||
_questSchema = JsonSchema.FromStream(AssemblyQuestLoader.QuestSchema).AsTask().Result;
|
||||
}
|
||||
if (_questNodes.TryGetValue(quest.Id, out JsonNode value) && !_questSchema.Evaluate(value, new EvaluationOptions
|
||||
{
|
||||
Culture = CultureInfo.InvariantCulture,
|
||||
OutputFormat = OutputFormat.List
|
||||
}).IsValid)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = null,
|
||||
Step = null,
|
||||
Type = EIssueType.InvalidJsonSchema,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "JSON Validation failed"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public void Enqueue(ElementId elementId, JsonNode questNode)
|
||||
{
|
||||
_questNodes[elementId] = questNode;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_questNodes.Clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class NextQuestValidator : IQuestValidator
|
||||
{
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
foreach (var item in from x in quest.AllSteps()
|
||||
where x.Step.NextQuestId == quest.Id
|
||||
select x)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = item.Item1.Sequence,
|
||||
Step = item.Item2,
|
||||
Type = EIssueType.InvalidNextQuestId,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Next quest should not reference itself"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System.Collections.Generic;
|
||||
using Questionable.Model;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class QuestDisabledValidator : IQuestValidator
|
||||
{
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
if (quest.Root.Disabled)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = null,
|
||||
Step = null,
|
||||
Type = EIssueType.QuestDisabled,
|
||||
Severity = EIssueSeverity.None,
|
||||
Description = "Quest is disabled"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lumina.Text.ReadOnly;
|
||||
using Questionable.Functions;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class SayValidator : IQuestValidator
|
||||
{
|
||||
private readonly ExcelFunctions _excelFunctions;
|
||||
|
||||
public SayValidator(ExcelFunctions excelFunctions)
|
||||
{
|
||||
_excelFunctions = excelFunctions;
|
||||
}
|
||||
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
foreach (var item in from x in quest.AllSteps()
|
||||
where x.Step.InteractionType == EInteractionType.Say
|
||||
select x)
|
||||
{
|
||||
ChatMessage chatMessage = item.Item3.ChatMessage;
|
||||
if (chatMessage != null)
|
||||
{
|
||||
ReadOnlySeString? rawDialogueText = _excelFunctions.GetRawDialogueText(quest, chatMessage.ExcelSheet, chatMessage.Key);
|
||||
if (rawDialogueText.HasValue && rawDialogueText.Value.PayloadCount != 1)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = item.Item1.Sequence,
|
||||
Step = item.Item2,
|
||||
Type = EIssueType.InvalidChatMessage,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = $"Invalid chat message: {rawDialogueText.Value}"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Questionable.Data;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class SinglePlayerInstanceValidator : IQuestValidator
|
||||
{
|
||||
private readonly Dictionary<ElementId, List<byte>> _questIdToDutyIndexes;
|
||||
|
||||
public SinglePlayerInstanceValidator(TerritoryData territoryData)
|
||||
{
|
||||
_questIdToDutyIndexes = (from x in territoryData.GetAllQuestsWithQuestBattles()
|
||||
group x by x.QuestId).ToDictionary((IGrouping<ElementId, (ElementId QuestId, byte Index, TerritoryData.ContentFinderConditionData Data)> x) => x.Key, (IGrouping<ElementId, (ElementId QuestId, byte Index, TerritoryData.ContentFinderConditionData Data)> x) => x.Select(((ElementId QuestId, byte Index, TerritoryData.ContentFinderConditionData Data) y) => y.Index).ToList());
|
||||
}
|
||||
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
if (!_questIdToDutyIndexes.TryGetValue(quest.Id, out List<byte> value))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (byte index in value)
|
||||
{
|
||||
if (!quest.AllSteps().Any<(QuestSequence, int, QuestStep)>(((QuestSequence Sequence, int StepId, QuestStep Step) x) => x.Step.InteractionType == EInteractionType.SinglePlayerDuty && x.Step.SinglePlayerDutyIndex == index))
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = null,
|
||||
Step = null,
|
||||
Type = EIssueType.UnusedSinglePlayerInstance,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = $"Single player instance {index} not used"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class UniqueSinglePlayerInstanceValidator : IQuestValidator
|
||||
{
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
List<(QuestSequence, int, byte)> list = (from x in quest.AllSteps()
|
||||
where x.Step.InteractionType == EInteractionType.SinglePlayerDuty
|
||||
select (Sequence: x.Sequence, StepId: x.StepId, SinglePlayerDutyIndex: x.Step.SinglePlayerDutyIndex)).ToList();
|
||||
if (list.DistinctBy<(QuestSequence, int, byte), byte>(((QuestSequence Sequence, int StepId, byte SinglePlayerDutyIndex) x) => x.SinglePlayerDutyIndex).Count() >= list.Count)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
foreach (var item in list)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = item.Item1.Sequence,
|
||||
Step = item.Item2,
|
||||
Type = EIssueType.DuplicateSinglePlayerInstance,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = $"Duplicate singleplayer duty index: {item.Item3}"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class UniqueStartStopValidator : IQuestValidator
|
||||
{
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
ElementId id = quest.Id;
|
||||
if ((id is SatisfactionSupplyNpcId || id is AlliedSocietyDailyId) ? true : false)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
int num = 1;
|
||||
List<EInteractionType> list = new List<EInteractionType>(num);
|
||||
CollectionsMarshal.SetCount(list, num);
|
||||
Span<EInteractionType> span = CollectionsMarshal.AsSpan(list);
|
||||
int index = 0;
|
||||
span[index] = EInteractionType.AcceptQuest;
|
||||
List<(QuestSequence Sequence, int StepId, QuestStep Step)> questAccepts = (from x in FindQuestStepsWithInteractionType(quest, list)
|
||||
where x.Step.PickUpQuestId == null
|
||||
select x).ToList();
|
||||
foreach (var item in questAccepts)
|
||||
{
|
||||
if (item.Sequence.Sequence != 0 || item.StepId != quest.FindSequence(0).Steps.Count - 1)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = item.Sequence.Sequence,
|
||||
Step = item.StepId,
|
||||
Type = EIssueType.UnexpectedAcceptQuestStep,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Unexpected AcceptQuest step"
|
||||
};
|
||||
}
|
||||
}
|
||||
if (quest.FindSequence(0) != null && questAccepts.Count == 0)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = 0,
|
||||
Step = null,
|
||||
Type = EIssueType.MissingQuestAccept,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "No AcceptQuest step"
|
||||
};
|
||||
}
|
||||
index = 1;
|
||||
List<EInteractionType> list2 = new List<EInteractionType>(index);
|
||||
CollectionsMarshal.SetCount(list2, index);
|
||||
span = CollectionsMarshal.AsSpan(list2);
|
||||
num = 0;
|
||||
span[num] = EInteractionType.CompleteQuest;
|
||||
List<(QuestSequence Sequence, int StepId, QuestStep Step)> questCompletes = (from x in FindQuestStepsWithInteractionType(quest, list2)
|
||||
where x.Step.TurnInQuestId == null
|
||||
select x).ToList();
|
||||
foreach (var item2 in questCompletes)
|
||||
{
|
||||
if (item2.Sequence.Sequence != byte.MaxValue || item2.StepId != quest.FindSequence(byte.MaxValue).Steps.Count - 1)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = item2.Sequence.Sequence,
|
||||
Step = item2.StepId,
|
||||
Type = EIssueType.UnexpectedCompleteQuestStep,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Unexpected CompleteQuest step"
|
||||
};
|
||||
}
|
||||
}
|
||||
if (quest.FindSequence(byte.MaxValue) != null && questCompletes.Count == 0)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = (byte)byte.MaxValue,
|
||||
Step = null,
|
||||
Type = EIssueType.MissingQuestComplete,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "No CompleteQuest step"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<(QuestSequence Sequence, int StepId, QuestStep Step)> FindQuestStepsWithInteractionType(Quest quest, List<EInteractionType> interactionType)
|
||||
{
|
||||
return from x in quest.AllSteps()
|
||||
where interactionType.Contains(x.Step.InteractionType)
|
||||
select x;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue