using AssortHelpers.Common.Helpers; using AssortHelpers.Helpers; using AssortValidator.Common; using AssortValidator.Common.Helpers; using AssortValidator.Common.Helpers.Models; using AssortValidator.Common.Models; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace AssortValidator { class Program { static void Main(string[] args) { var inputPath = CreateWorkingFolders(); InputFileHelper.SetInputFiles(inputPath); var assortData = AssortHelper.GetAssortData(); var liveAssortData = AssortHelper.GetLiveAssortData(); ListAssortFilesFound(assortData); ListAssortsInLiveNotInOffline(assortData, liveAssortData); CheckAssortValues(assortData, liveAssortData); } private static void ListAssortFilesFound(Dictionary assortData) { foreach (var trader in Enum.GetValues(typeof(Trader)).Cast()) { var traderFound = assortData.ContainsKey(trader); if (traderFound) { LoggingHelpers.LogSuccess($"Trader: {trader} found"); } else { LoggingHelpers.LogError($"Trader: {trader} NOT found"); } } } private static void ListAssortsInLiveNotInOffline(Dictionary assortData, Dictionary liveAssortData) { foreach (var trader in liveAssortData) { LoggingHelpers.LogInfo($"Checking Trader: {trader.Key}"); LoggingHelpers.LogInfo($"Live assort count: {trader.Value.items.Count}"); var missingAssortCount = 0; var missingItems = new List(); foreach (var liveAssort in trader.Value.items.Where(x => x.parentId == "hideout")) { var offlineAssort = assortData[trader.Key].items.FirstOrDefault(x=>x._tpl == liveAssort._tpl); if (offlineAssort == null) { missingAssortCount++; var itemName = ItemTemplateHelper.GetTemplateById(liveAssort._tpl)._name; int assortLoyaltyLevel = GetAssortLoyaltyLevel(liveAssort._id, trader.Value); var liveBarterData = trader.Value.barter_scheme.FirstOrDefault(x => x.Key == liveAssort._id).Value; var firstItemIsMoney = ItemIsMoney(liveBarterData.First().First()._tpl); var isMoneyMessage = firstItemIsMoney ? "money trade" : "barter trade"; missingItems.Add($"{liveAssort._tpl} - {itemName} - Level {assortLoyaltyLevel} - {isMoneyMessage}"); } } if (missingAssortCount > 0) { LoggingHelpers.LogInfo($"Assorts in live but not in offline: {missingAssortCount}"); foreach (var item in missingItems) { LoggingHelpers.LogError($"{item}"); } } else { LoggingHelpers.LogSuccess("no missing assorts found in offline when compared to live :)"); } } } private static void CheckAssortValues(Dictionary assortData, Dictionary liveAssortData) { List questAssortUnlocks = GetQuestsThatUnlockAssorts(); foreach (var trader in assortData) { LoggingHelpers.LogHeading($"Checking Trader: {trader.Key}"); var correspondingLiveTrader = liveAssortData[trader.Key]; if (correspondingLiveTrader == null) { LoggingHelpers.LogError($"Unable to find live trader: {trader.Key}"); LoggingHelpers.LogNewLine(); continue; } foreach (var assort in trader.Value.items.Where(x=>x.parentId == "hideout")) { var itemName = ItemTemplateHelper.GetTemplateById(assort._tpl)._name; int assortLoyaltyLevel = AssortHelper.GetAssortLoyaltyLevel(assort._id, trader.Value); LoggingHelpers.LogInfo($"{trader.Key}: itemId: {assort._id} - tpId: {assort._tpl} ({itemName}) Level {assortLoyaltyLevel}"); LogUnlockQuest(questAssortUnlocks, assort, assortLoyaltyLevel); var liveAssort = GetLiveAssort(correspondingLiveTrader, assort._tpl, assortLoyaltyLevel); if (liveAssort == null) { continue; } // Check if upd objects match ( both exist or both dont exist) if (!DoesLiveAssortUpdMatchOffline(assort, liveAssort, itemName)) { continue; } var offlineBarterData = trader.Value.barter_scheme.FirstOrDefault(x => x.Key == assort._id).Value; var liveBarterData = correspondingLiveTrader.barter_scheme.FirstOrDefault(x => x.Key == liveAssort._id).Value; if (assort.upd != null) { CheckUpdValues(assort, liveAssort); // Iterate over assort barter items // Assorts live barter data for (int i = 0; i < offlineBarterData.Count; i++) { var liveItem = liveBarterData[i]; // Create list of offline barter items for assort var offlineBarterItems = new List(); foreach (var foundItem in offlineBarterData) { foreach (var item in foundItem) { offlineBarterItems.Add(item); } } LogAssortType(offlineBarterItems); if (offlineBarterItems.Count != liveItem.Count) { LoggingHelpers.LogError($"assort {assort._id} barter item count mismatch, found {offlineBarterItems.Count}, expected {liveItem.Count}"); LoggingHelpers.LogError("Found:"); foreach (var foundItem in offlineBarterItems) { LoggingHelpers.LogError($"{foundItem._tpl} ({ItemTemplateHelper.GetTemplateById(foundItem._tpl)._name}) x{foundItem.count}"); } LoggingHelpers.LogError("Expected:"); foreach (var missingItem in liveItem) { LoggingHelpers.LogError($"{missingItem._tpl} ({ItemTemplateHelper.GetTemplateById(missingItem._tpl)._name}) x{missingItem.count}"); } continue; } // Iterate over the barter requirement items for (int j = 0; j < offlineBarterData[i].Count; j++) { // Check live barter item exists if (j > liveItem.Count - 1) { LoggingHelpers.LogError($"assort {assort._id} live barter item requirement missing"); continue; } var liveSubItem = liveItem[j]; var subItem = liveItem[j]; ValueCheckerHelper.CheckValuesMatch(subItem._tpl, liveSubItem._tpl, "barter item template ids dont match"); ValueCheckerHelper.CheckValuesMatch(subItem.count.ToString(), liveSubItem.count.ToString(), "barter item counts dont match"); } } } CheckAssortCost(offlineBarterData, liveBarterData); LoggingHelpers.LogNewLine(); } } } /// /// Log the quest type: money or barter trade /// private static void LogAssortType(List offlineBarterItems) { if (ItemIsMoney(offlineBarterItems.First()._tpl)) { LoggingHelpers.LogInfo("assort is money trade"); } else { LoggingHelpers.LogInfo("assort is barter trade"); } } private static void LogUnlockQuest(List questAssortUnlocks, Item assort, int assortLoyaltyLevel) { var questAssortUnlockData = questAssortUnlocks.FirstOrDefault(x => x.AssortTemplateId == assort._tpl && x.LoyaltyLevel == assortLoyaltyLevel); if (questAssortUnlockData != null) { LoggingHelpers.LogInfo($"Assort likely unlocked by quest: {questAssortUnlockData.QuestName} ({questAssortUnlockData.QuestId})"); } } private static bool ItemIsMoney(string templateId) { return ItemTemplateHelper.GetTemplateById(templateId)._parent == "543be5dd4bdc2deb348b4569"; // money type } private static bool DoesLiveAssortUpdMatchOffline(Item assort, LiveAssort.Item correspondingLiveAssort, string itemName) { if (assort.upd != null && correspondingLiveAssort.upd == null) { LoggingHelpers.LogError($"assort {assort._id} ({itemName}) has a upd object, live does not. skipping assort"); LoggingHelpers.LogNewLine(); return false; } // no parent id = not a gun item if (assort.parentId == null && assort.upd == null && correspondingLiveAssort.upd != null) { LoggingHelpers.LogError($"assort {assort._id} ({itemName}) does not have a upd object, live does. skipping assort"); LoggingHelpers.LogNewLine(); return false; } return true; } private static int GetAssortLoyaltyLevel(string assortId, LiveAssort assort) { int assortLoyaltyLevel = 1; if (!assort.loyal_level_items.ContainsKey(assortId)) { LoggingHelpers.LogWarning($"No offline loyalty level record found for itemId: {assortId} assuming level 1"); LoggingHelpers.LogNewLine(); } else { assortLoyaltyLevel = assort.loyal_level_items[assortId]; } return assortLoyaltyLevel; } private static void CheckUpdValues(Item assort, LiveAssort.Item correspondingLiveAssort) { if (assort.upd.StackObjectsCount.HasValue && !assort.upd.UnlimitedCount) { var liveStackCount = GetRoundedStackCount(correspondingLiveAssort.upd.StackObjectsCount); ValueCheckerHelper.CheckValuesMatch(assort.upd.StackObjectsCount.Value, liveStackCount, $"stackobjectCount does not match live. orig: ({correspondingLiveAssort.upd.StackObjectsCount})"); } // check count is 999999 if unlimited count is true if (assort.upd.UnlimitedCount) { ValueCheckerHelper.CheckValuesMatch(assort.upd.StackObjectsCount.Value, 999999, "unlimited count does not match"); } // Check max buy restriction matches ValueCheckerHelper.CheckValuesMatch(assort.upd.BuyRestrictionMax.GetValueOrDefault(0), correspondingLiveAssort.upd.BuyRestrictionMax.GetValueOrDefault(0), "BuyRestrictionMax does not match"); } private static LiveAssort.Item GetLiveAssort(LiveAssort trader, string templateId, int expectedLoyaltyLevel) { var liveAssorts = trader.items; var liveLoyaltyLevels = trader.loyal_level_items; var liveAssortsWithTemplateId = liveAssorts.Where(x => x._tpl == templateId).ToList(); if (liveAssortsWithTemplateId.Count == 0) { LoggingHelpers.LogError($"Unable to find live assort tpId: {templateId} ({ItemTemplateHelper.GetTemplateById(templateId)._name})"); LoggingHelpers.LogError($"Skipping assort"); LoggingHelpers.LogNewLine(); return null; } // Only one item, break out early and return if (liveAssortsWithTemplateId.Count == 1) { return liveAssortsWithTemplateId.First(); } // More than one assort found // Gather assort ids and use them to get loyalty level records var liveAssortsIds = liveAssortsWithTemplateId.Select(x => x._id); var liveLoyaltyLevelsForTemplateId = liveLoyaltyLevels.Where(x => liveAssortsIds.Contains(x.Key)).ToList(); // Same loyalty level + multiple found if (liveLoyaltyLevelsForTemplateId.All(x => x.Value == expectedLoyaltyLevel) && liveLoyaltyLevelsForTemplateId.Count > 1) { // Both have same loyalty level, cant proceed; LoggingHelpers.LogError($"Multiple ({liveLoyaltyLevelsForTemplateId.Count}) live assorts for this tpId found at same Level ({expectedLoyaltyLevel}) - Unable to distinguish: {templateId} ({ItemTemplateHelper.GetTemplateById(templateId)._name}) "); LoggingHelpers.LogNewLine(); return null; } var loyaltyLevelItemThatMatches = liveLoyaltyLevelsForTemplateId.Where(x => x.Value == expectedLoyaltyLevel); if (loyaltyLevelItemThatMatches != null && loyaltyLevelItemThatMatches.Count() > 1) { LoggingHelpers.LogWarning($"({loyaltyLevelItemThatMatches.Count()}) live items found, choosing first one in list"); } if (loyaltyLevelItemThatMatches == null) { // Both have same loyalty level, cant proceed; LoggingHelpers.LogError($"No live assort for this tpId found at same level - Unable proceed: {templateId} ({ItemTemplateHelper.GetTemplateById(templateId)._name})"); LoggingHelpers.LogNewLine(); return null; } return liveAssorts.Find(x => x._id == loyaltyLevelItemThatMatches.FirstOrDefault().Key); } private static int GetRoundedStackCount(int stackObjectsCount) { //TODO: automate this with math.round if (stackObjectsCount < 6) { return 5; } if (stackObjectsCount < 11) { return 10; } if (stackObjectsCount < 21) { return 20; } if (stackObjectsCount < 51) { return 50; } if (stackObjectsCount < 101) { return 100; } if (stackObjectsCount < 201) { return 200; } if (stackObjectsCount < 501) { return 500; } if (stackObjectsCount < 1001) { return 1000; } if (stackObjectsCount < 2001) { return 2000; } if (stackObjectsCount < 3001) { return 3000; } if (stackObjectsCount < 4001) { return 4000; } if (stackObjectsCount < 5001) { return 5000; } if (stackObjectsCount < 6001) { return 6000; } if (stackObjectsCount < 10001) { return 10000; } if (stackObjectsCount < 15001) { return 15000; } if (stackObjectsCount < 20001) { return 20000; } if (stackObjectsCount < 25001) { return 25000; } if (stackObjectsCount < 50001) { return 50000; } if (stackObjectsCount < 70001) { return 70000; } if (stackObjectsCount < 85001) { return 85000; } if (stackObjectsCount < 100001) { return 100000; } if (stackObjectsCount < 150001) { return 150000; } if (stackObjectsCount < 200001) { return 200000; } if (stackObjectsCount < 500001) { return 500000; } if (stackObjectsCount < 700001) { return 700000; } if (stackObjectsCount < 1000001) { return 1000000; } if (stackObjectsCount < 5000001) { return 5000000; } return 9999999; } private static void CheckAssortCost(List> offlineBarterData, List> liveBarterData) { var firstBarterItem = offlineBarterData.First().First(); var firstOfflineItemIsMoney = ItemIsMoney(firstBarterItem._tpl); var firstLiveBarterItem = liveBarterData.First().First(); var firstLiveItemIsMoney = ItemIsMoney(firstLiveBarterItem._tpl); if (firstOfflineItemIsMoney != firstLiveItemIsMoney) { LoggingHelpers.LogError($"item cost mismatch. Offline item is money: {firstOfflineItemIsMoney}. Live item is money: {firstLiveItemIsMoney}"); } if (firstOfflineItemIsMoney && firstOfflineItemIsMoney) { var liveCount = int.Parse(Math.Round(firstLiveBarterItem.count).ToString()); ValueCheckerHelper.CheckValuesMatch(firstBarterItem.count, liveCount, "costs do not match"); } } private static string CreateWorkingFolders() { var workingPath = Directory.GetCurrentDirectory(); // create input folder var inputPath = $"{workingPath}//input"; DiskHelpers.CreateDirIfDoesntExist(inputPath); return inputPath; } private static List GetQuestsThatUnlockAssorts() { var liveQuests = QuestHelper.GetLiveQuestData(); return QuestHelper.GetQuestAssortUnlocks(liveQuests.data); } } }