using AssortGenerator.Common.Helpers;
using QuestValidator.Common;
using QuestValidator.Common.Helpers;
using QuestValidator.Helpers;
using QuestValidator.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace QuestValidator
{
class Program
{
static void Main(string[] args)
{
var inputPath = DiskHelpers.CreateWorkingFolders();
InputFileHelper.SetInputFiles(inputPath);
//read in quest file
var questData = QuestHelper.GetQuestData();
var liveQuestData = QuestHelper.GetLiveQuestData();
if (questData == null || liveQuestData == null)
{
LoggingHelpers.LogError("Unable to read quest data. Are you sure the both quest files are in 'QuestValidator//bin//Debug//netcoreapp3.1//input'");
return;
}
CheckForMissingQuestsInAkiFile(liveQuestData, questData);
foreach (var item in questData)
{
var quest = item.Value;
LogQuestDetails(quest);
// Get live quest
var relatedLiveQuest = liveQuestData.data.FirstOrDefault(x => x._id == quest._id);
if (!ItemExists(relatedLiveQuest, "live quest. Live dump too old ?"))
{
LoggingHelpers.LogInfo("");
continue;
}
CheckRootItemValues(quest, relatedLiveQuest);
CheckSuccessRewardItems(quest, relatedLiveQuest);
CheckStartedRewardItems(quest, relatedLiveQuest);
CheckAvailableForFinishConditionItems(quest, relatedLiveQuest);
LoggingHelpers.LogInfo("");
LoggingHelpers.LogInfo("-----");
LoggingHelpers.LogInfo("");
}
}
private static void LogQuestDetails(Quest quest)
{
var questName = QuestHelper.GetQuestNameById(quest._id);
LoggingHelpers.LogInfo($"### Quest name: {questName} ({quest._id})");
LoggingHelpers.LogInfo($"Wiki: https://escapefromtarkov.fandom.com/wiki/{questName.Replace(' ', '_')}");
}
private static void CheckRootItemValues(Quest quest, Quest relatedLiveQuest)
{
// Check image id matches
CheckValuesMatch(quest.image.Substring(0, quest.image.Length - 4), relatedLiveQuest?.image.Substring(0, relatedLiveQuest.image.Length - 4), "item path mismatch");
// Check started reward count matches
CheckValuesMatch(quest.rewards.Started.Count, relatedLiveQuest.rewards.Started.Count, "Started item count mismatch");
// Check success reward count matches
CheckValuesMatch(quest.rewards.Success.Count, relatedLiveQuest.rewards.Success.Count, "success item count mismatch");
// Check Fail reward count matches
CheckValuesMatch(quest.rewards.Fail.Count, relatedLiveQuest.rewards.Fail.Count, "fail item count mismatch");
// Check min level matches
CheckValuesMatch(quest.min_level, relatedLiveQuest.min_level, "min level value mismatch");
// Check location matches
CheckValuesMatch(quest.location, relatedLiveQuest.location, "location value mismatch");
// Check traderid matches
CheckValuesMatch(quest.traderId, relatedLiveQuest.traderId, "traderid value mismatch");
// Check type matches
CheckValuesMatch(quest.type, relatedLiveQuest.type, "quest type value mismatch");
}
private static void CheckSuccessRewardItems(Quest quest, Quest relatedLiveQuest)
{
var liveQuestSuccessRewardItems = relatedLiveQuest.rewards.Success;
foreach (RewardStatus questSuccessRewardItem in quest.rewards.Success.Where(x => x.type == "Item"))
{
// Get live reward item by index and type
var relatedLiveRewardItem = GetLiveRewardItem(questSuccessRewardItem, liveQuestSuccessRewardItems);
LogUnableToFindSuccessItemInLiveData(questSuccessRewardItem, relatedLiveRewardItem);
if (relatedLiveRewardItem == null)
{
continue;
}
// Ensure target matches the objects items[0].id value
if (questSuccessRewardItem.items[0]?._id != questSuccessRewardItem.target)
{
LoggingHelpers.LogWarning($"WARNING target does not match first item: {questSuccessRewardItem.target}, expected {questSuccessRewardItem.items[0]?._id}");
}
// Check template ids match
CheckValuesMatch(questSuccessRewardItem.items[0]._tpl, relatedLiveRewardItem.items[0]._tpl, "mismatch for template id", questSuccessRewardItem.items[0]._id, true);
// Check value values match
CheckValuesMatch(questSuccessRewardItem.value, relatedLiveRewardItem.value, "mismatch for success item reward value", questSuccessRewardItem.id);
// Check item stack count
if (questSuccessRewardItem.items[0] != null && questSuccessRewardItem.items[0].upd != null)
{
CheckValuesMatch(questSuccessRewardItem.items[0].upd.StackObjectsCount, questSuccessRewardItem.items[0].upd.StackObjectsCount, "mismatch for success item StackObjectsCount", questSuccessRewardItem.items[0]._id);
}
// check sub items match
CheckSubItemsMatch(questSuccessRewardItem, relatedLiveRewardItem);
}
foreach (var questSuccessRewardItem in quest.rewards.Success.Where(x => x.type == "Experience"))
{
var relatedLiveRewardItem = liveQuestSuccessRewardItems.FirstOrDefault(x => x.type == "Experience");
if (!ItemExists(relatedLiveRewardItem, "experience success reward item", questSuccessRewardItem.index))
{
continue;
}
// check experience value matches
CheckValuesMatch(questSuccessRewardItem.value, relatedLiveRewardItem.value, "experience value mismatch");
}
foreach (var questSuccessRewardItem in quest.rewards.Success.Where(x => x.type == "TraderStanding"))
{
var relatedLiveRewardItem = liveQuestSuccessRewardItems.FirstOrDefault(x => x.target == questSuccessRewardItem.target && x.type == "TraderStanding");
if (!ItemExists(relatedLiveRewardItem, "TraderStanding success reward item", questSuccessRewardItem.index))
{
continue;
}
// check standing value matches
CheckValuesMatch(questSuccessRewardItem.value, relatedLiveRewardItem.value, "trader standing value mismatch");
// check target value matches
CheckValuesMatch(questSuccessRewardItem.target, relatedLiveRewardItem.target, "trader target value mismatch");
}
foreach (var questSuccessRewardItem in quest.rewards.Success.Where(x => x.type == "AssortmentUnlock"))
{
var relatedLiveRewardItem = liveQuestSuccessRewardItems.Find(x => x.id == questSuccessRewardItem.id && x.type == "AssortmentUnlock");
if (relatedLiveRewardItem == null)
{
relatedLiveRewardItem = liveQuestSuccessRewardItems.Find(x => x.traderId == questSuccessRewardItem.traderId
&& x.index == questSuccessRewardItem.index
&& x.type == "AssortmentUnlock"
&& x.items[0]._tpl == questSuccessRewardItem.items[0]._tpl);
}
if (!ItemExists(relatedLiveRewardItem, "AssortmentUnlock success reward item", questSuccessRewardItem.index))
{
continue;
}
// Check loyalty level
CheckValuesMatch(questSuccessRewardItem.loyaltyLevel.Value, relatedLiveRewardItem.loyaltyLevel.Value, "loyalty level value mismatch", questSuccessRewardItem.id);
// Check traderId
CheckValuesMatch(questSuccessRewardItem.traderId, relatedLiveRewardItem.traderId, "traderId value mismatch", questSuccessRewardItem.id);
// check target equals items[0].id
CheckValuesMatch(questSuccessRewardItem.target, questSuccessRewardItem.items[0]._id, "target value does not match items[0].id mismatch", questSuccessRewardItem.id);
}
}
private static void CheckSubItemsMatch(RewardStatus questSuccessRewardItem, RewardStatus relatedLiveRewardItem)
{
foreach (var subItem in questSuccessRewardItem.items.Where(x => !string.IsNullOrEmpty(x.slotId)))
{
// find matching live counterpart by slotid
var liveCounterpart = relatedLiveRewardItem.items.FirstOrDefault(x => x.slotId == subItem.slotId);
if (liveCounterpart == null)
{
LoggingHelpers.LogWarning($"a live counterpart for the subItem {subItem.slotId} could not be found, skipping subItem check");
continue;
}
CheckValuesMatch(subItem._tpl, liveCounterpart._tpl, $"mismatch for success subItem({subItem.slotId}) reward templateId", subItem._id);
}
}
private static void LogUnableToFindSuccessItemInLiveData(RewardStatus questSuccessRewardItem, RewardStatus relatedLiveRewardItem)
{
if (relatedLiveRewardItem == null)
{
LoggingHelpers.LogError($"ERROR unable to find success reward item in live quest data by index: ({questSuccessRewardItem.index}) OR template id: {questSuccessRewardItem.items[0]._tpl} ({ItemTemplateHelper.GetTemplateById(questSuccessRewardItem.items[0]._tpl)._name})");
LoggingHelpers.LogError("Existing items:");
LogSuccessItems(questSuccessRewardItem);
LoggingHelpers.LogError($"ERROR Skipping quest success item. id: {questSuccessRewardItem.id}");
}
}
private static void LogSuccessItems(RewardStatus rewardItem)
{
foreach (var item in rewardItem.items)
{
LoggingHelpers.LogInfo($"{item._tpl} ({ItemTemplateHelper.GetTemplateById(item._tpl)._name})");
}
}
///
/// Find live success item reward by index
/// If item at index does not match templateId to desired item
/// get live success item reward by template id
///
///
///
///
private static RewardStatus GetLiveRewardItem(RewardStatus questSuccessRewardItem, List liveQuestSuccessRewardItems)
{
var LiveItemRewards = liveQuestSuccessRewardItems.Where(x => x.type == "Item");
var liveRewardItemByIndex = LiveItemRewards.FirstOrDefault(x => x.index == questSuccessRewardItem.index);
// item found by index but template id didnt match
if (liveRewardItemByIndex != null && liveRewardItemByIndex.items[0]._tpl != questSuccessRewardItem.items[0]._tpl)
{
return LiveItemRewards.FirstOrDefault(x => x.items[0]._tpl == questSuccessRewardItem.items[0]._tpl);
}
return liveRewardItemByIndex;
}
private static void CheckStartedRewardItems(Quest quest, Quest relatedLiveQuest)
{
var liveQuestStartedRewardItems = relatedLiveQuest.rewards.Started;
foreach (var questStartedRewardItem in quest.rewards.Started.Where(x => x.type == "Item"))
{
var errorMessage = string.Empty;
// Get live reward item by index and type
var relatedLiveRewardItem = liveQuestStartedRewardItems.Find(x => x.index == questStartedRewardItem.index && x.type == "Item");
if (relatedLiveRewardItem == null)
{
// Get live reward item by templateId and type as we cant find it by index
relatedLiveRewardItem = liveQuestStartedRewardItems.Find(x => x.items != null && x.items[0]?._tpl == questStartedRewardItem.items[0]?._tpl && x.type == "Item");
if (relatedLiveRewardItem == null)
{
LoggingHelpers.LogError($"ERROR unable to find started reward item in live quest data by index: ({questStartedRewardItem.index}) OR template id: {questStartedRewardItem.items[0]._tpl}");
LoggingHelpers.LogError($"ERROR Skipping quest started item: {questStartedRewardItem.id}");
continue;
}
}
// Ensure target matches the objects items[0].id value
if (questStartedRewardItem.items[0]?._id != questStartedRewardItem.target)
{
LoggingHelpers.LogWarning($"WARNING target does not match first item: {questStartedRewardItem.target}, expected {questStartedRewardItem.items[0]?._id}");
}
// Check template ids match
CheckValuesMatch(questStartedRewardItem.items[0]._tpl, relatedLiveRewardItem.items[0]._tpl, "mismatch for template id", questStartedRewardItem.items[0]._id, true);
// Check value values match
CheckValuesMatch(questStartedRewardItem.value, relatedLiveRewardItem.value, "mismatch for success item reward value", questStartedRewardItem.id);
// Check item stack count
if (questStartedRewardItem.items[0] != null && questStartedRewardItem.items[0].upd != null)
{
CheckValuesMatch(questStartedRewardItem.items[0].upd.StackObjectsCount, relatedLiveRewardItem.items[0].upd.StackObjectsCount, "mismatch for started item StackObjectsCount", questStartedRewardItem.items[0]._id);
}
}
foreach (var questStartedRewardItem in quest.rewards.Started.Where(x => x.type == "AssortmentUnlock"))
{
// Get live reward item by id
var relatedLiveRewardItem = liveQuestStartedRewardItems.FirstOrDefault(x => x.id == questStartedRewardItem.id && x.type == "AssortmentUnlock");
if (relatedLiveRewardItem == null)
{
// Cant find live reward item by id, get my template id inside items[0]
relatedLiveRewardItem = liveQuestStartedRewardItems.Find(x => x.traderId == questStartedRewardItem.traderId
&& x.index == questStartedRewardItem.index
&& x.type == "AssortmentUnlock"
&& x.items[0]._tpl == questStartedRewardItem.items[0]._tpl);
}
if (!ItemExists(relatedLiveRewardItem, "AssortmentUnlock started reward item", questStartedRewardItem.index))
{
continue;
}
// Check loyalty level
CheckValuesMatch(questStartedRewardItem.loyaltyLevel.Value, relatedLiveRewardItem.loyaltyLevel.Value, "loyalty level value mismatch", questStartedRewardItem.id);
// Check traderId
CheckValuesMatch(questStartedRewardItem.traderId, relatedLiveRewardItem.traderId, "traderId value mismatch", questStartedRewardItem.id);
// check target equals items[0].id
CheckValuesMatch(questStartedRewardItem.target, questStartedRewardItem.items[0]._id, "target value does not match items[0].id mismatch", questStartedRewardItem.id);
}
}
private static void CheckAvailableForFinishConditionItems(Quest quest, Quest relatedLiveQuest)
{
foreach (var availableForFinishItem in quest.conditions.AvailableForFinish)
{
AvailableFor liveFinishItem = relatedLiveQuest.conditions.AvailableForFinish.Find(x => x._props.id == availableForFinishItem._props.id);
if (!ItemExists(liveFinishItem, "AvailableForFinish item", availableForFinishItem._props.index.Value))
{
continue;
}
// Check parentId
CheckValuesMatch(availableForFinishItem._props.parentId, liveFinishItem._props.parentId, "AvailableForFinish parentId mismatch", availableForFinishItem._props.id);
// check AvailableForFinish resetOnSessionEnd
CheckValuesMatch(availableForFinishItem._props.resetOnSessionEnd, liveFinishItem._props.resetOnSessionEnd, "AvailableForFinish resetOnSessionEnd value mismatch", availableForFinishItem._props.id);
// check AvailableForFinish target
CheckValuesMatch(Convert.ToString(availableForFinishItem._props.target), Convert.ToString(liveFinishItem._props.target), "AvailableForFinish target value mismatch", availableForFinishItem._props.id);
// check weapons allowed match
//CheckValuesMatch(availableForFinishItem._props.weapon.Count, liveFinishItem._props.counter.conditions), "AvailableForFinish target value mismatch", availableForFinishItem._props.id);
}
}
private static bool ItemExists(object itemToCheck, string message, int index = -1)
{
if (itemToCheck == null)
{
if (index == -1)
{
LoggingHelpers.LogError($"ERROR no match found for {message}");
}
else
{
LoggingHelpers.LogError($"ERROR no match found for {message} at index: {index}");
}
return false;
}
return true;
}
private static void CheckValuesMatch(int firstValue, int secondValue, string message, string associatedId = "")
{
if (firstValue != secondValue)
{
if (associatedId == string.Empty)
{
LoggingHelpers.LogWarning($"WARNING {message}: '{firstValue}', expected '{secondValue}'");
}
else
{
LoggingHelpers.LogWarning($"WARNING {associatedId} {message}: '{firstValue}', expected '{secondValue}'");
}
}
}
private static void CheckValuesMatch(T firstValue, T secondValue, string message, string associatedId = "") where T : struct
{
if (firstValue.Equals(secondValue))
{
if (associatedId == string.Empty)
{
LoggingHelpers.LogWarning($"WARNING {message}: '{firstValue}', expected '{secondValue}'");
}
else
{
LoggingHelpers.LogWarning($"WARNING {associatedId} {message}: '{firstValue}', expected '{secondValue}'");
}
}
}
private static void CheckValuesMatch(string firstValue, string secondValue, string message, string associatedId = "", bool performTemplateIdLookup = false)
{
if (firstValue != secondValue)
{
if (performTemplateIdLookup)
{
firstValue = $"{firstValue} ({ItemTemplateHelper.GetTemplateById(firstValue)._name})";
secondValue = $"{secondValue} ({ItemTemplateHelper.GetTemplateById(secondValue)._name})";
}
if (associatedId == string.Empty)
{
LoggingHelpers.LogWarning($"WARNING {message}: '{firstValue}', expected '{secondValue}'");
}
else
{
LoggingHelpers.LogWarning($"WARNING {associatedId} {message}: '{firstValue}', expected '{secondValue}'");
}
}
}
private static void CheckValuesMatch(bool firstValue, bool secondValue, string message, string associatedId = "")
{
if (firstValue != secondValue)
{
if (associatedId == string.Empty)
{
LoggingHelpers.LogWarning($"WARNING {message}: '{firstValue}', expected '{secondValue}'");
}
else
{
LoggingHelpers.LogWarning($"WARNING {associatedId} {message}: '{firstValue}', expected '{secondValue}'");
}
}
}
private static void CheckForMissingQuestsInAkiFile(QuestRoot liveQuestData, Dictionary akiQuestData)
{
// iterate over live quests and look for quests that exist in live but not in aki
var missingQuests = new List();
foreach (var liveQuest in liveQuestData.data)
{
if (!akiQuestData.ContainsKey(liveQuest._id))
{
missingQuests.Add($"{liveQuest._id} {QuestHelper.GetQuestNameById(liveQuest._id)}");
}
}
// Quests in live but not in aki were found, log it
if (missingQuests.Count > 0)
{
LoggingHelpers.LogWarning($"WARNING aki quest list is missing quests:");
foreach (var item in missingQuests)
{
LoggingHelpers.LogWarning(item);
}
}
}
}
}