First commit of quest tool prototype

This commit is contained in:
Chomp 2021-09-07 17:45:49 +01:00
parent 665a4da751
commit f652c0e8b5
23 changed files with 250091 additions and 1 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
using System;
using System.IO;
namespace QuestValidator.Helpers
{
public static class DiskHelpers
{
public static void CreateDirIfDoesntExist(string path)
{
if (!Directory.Exists($"{path}"))
{
//create dump dir
Directory.CreateDirectory($"{path}");
}
}
public static string[] GetJsonFiles(string path)
{
return Directory.GetFiles(path, "*.json", SearchOption.TopDirectoryOnly);
}
}
}

View File

@ -0,0 +1,21 @@
using QuestValidator.Helpers;
using System.Collections.Generic;
using System.Linq;
namespace AssortGenerator.Common.Helpers
{
public static class InputFileHelper
{
private static List<string> _inputFilePaths;
public static void SetInputFiles(string path)
{
_inputFilePaths = DiskHelpers.GetJsonFiles(path).ToList();
}
public static IEnumerable<string> GetInputFilePaths()
{
return _inputFilePaths;
}
}
}

View File

@ -0,0 +1,45 @@
using QuestValidator.Common.Helpers;
using QuestValidator.Common.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
namespace QuestValidator.Common
{
public static class ItemTemplateHelper
{
private static Dictionary<string, Item> _itemCache;
public static Dictionary<string, Item> Items
{
get
{
if (_itemCache == null)
{
var itemsFilePath = $"{Directory.GetCurrentDirectory()}\\Assets\\items.json";
if (!File.Exists(itemsFilePath))
{
throw new Exception($"Missing items.json under assets ({itemsFilePath})");
}
var itemsJson = File.ReadAllText(itemsFilePath);
_itemCache = JsonSerializer.Deserialize<Dictionary<string, Item>>(itemsJson);
}
return _itemCache;
}
}
public static Item GetTemplateById(string templateId)
{
if (Items.ContainsKey(templateId))
{
return Items[templateId];
}
LoggingHelpers.LogToConsole($"Could not locate item template with id {templateId}", ConsoleColor.Red);
return null;
}
}
}

View File

@ -0,0 +1,45 @@
using QuestValidator.Common.Helpers;
using QuestValidator.Common.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
namespace QuestValidator.Common
{
public static class LiveQuestHelper
{
private static Dictionary<string, Item> _itemCache;
public static Dictionary<string, Item> Items
{
get
{
if (_itemCache == null)
{
var itemsFilePath = $"{Directory.GetCurrentDirectory()}\\Assets\\items.json";
if (!File.Exists(itemsFilePath))
{
throw new Exception($"Missing items.json under assets ({itemsFilePath})");
}
var itemsJson = File.ReadAllText(itemsFilePath);
_itemCache = JsonSerializer.Deserialize<Dictionary<string, Item>>(itemsJson);
}
return _itemCache;
}
}
public static Item GetTemplateById(string templateId)
{
if (Items.ContainsKey(templateId))
{
return Items[templateId];
}
LoggingHelpers.LogToConsole($"Could not locate item template with id {templateId}", ConsoleColor.Red);
return null;
}
}
}

View File

@ -0,0 +1,68 @@
using System;
namespace QuestValidator.Common.Helpers
{
public static class LoggingHelpers
{
public static string LogTimeTaken(double totalSeconds)
{
return Math.Round(totalSeconds, 2, MidpointRounding.ToEven).ToString();
}
public static void LogWarning(string message)
{
Console.BackgroundColor = ConsoleColor.DarkYellow;
Console.ForegroundColor = ConsoleColor.Black;
Console.Write(message);
ResetConsoleColours();
Console.WriteLine();
}
public static void LogInfo(string message)
{
Console.BackgroundColor = ConsoleColor.Gray;
Console.ForegroundColor = ConsoleColor.Black;
Console.Write(message);
ResetConsoleColours();
Console.WriteLine();
}
public static void LogSuccess(string message)
{
Console.BackgroundColor = ConsoleColor.Green;
Console.ForegroundColor = ConsoleColor.Black;
Console.Write(message);
ResetConsoleColours();
Console.WriteLine();
}
public static void LogError(string message)
{
Console.BackgroundColor = ConsoleColor.Red;
Console.ForegroundColor = ConsoleColor.White;
Console.Write(message);
ResetConsoleColours();
Console.WriteLine();
}
public static void LogToConsole(string message, ConsoleColor backgroundColour = ConsoleColor.Green)
{
Console.BackgroundColor = backgroundColour;
Console.ForegroundColor = ConsoleColor.Black;
Console.Write(message);
ResetConsoleColours();
Console.WriteLine();
}
private static void ResetConsoleColours()
{
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.White;
}
}
}

View File

@ -0,0 +1,310 @@
using QuestValidator.Models;
using QuestValidator.Models.Other;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
namespace AssortGenerator.Common.Helpers
{
public static class QuestHelper
{
private static readonly Dictionary<string, string> questNames = new Dictionary<string, string>
{
{ "5936d90786f7742b1420ba5b", "Debut" },
{ "5936da9e86f7742d65037edf", "Checking"},
{ "59674cd986f7744ab26e32f2", "Shootout picnic"},
{ "59674eb386f774539f14813a", "Delivery from the past"},
{ "5967530a86f77462ba22226b", "Bad rep evidence"},
{ "59675d6c86f7740a842fc482", "Ice cream cones"},
{ "59675ea386f77414b32bded2", "Postman Pat"},
{ "596760e186f7741e11214d58", "Postman Pat, p. 2"},
{ "5967725e86f774601a446662", "Shaking up teller"},
{ "5967733e86f774602332fc84", "Shortage"},
{ "59689ee586f7740d1570bbd5", "Sanitary Standards, p. 1"},
{ "59689fbd86f7740d137ebfc4", "Operation Aquarius"},
{ "5968eb3186f7741dde183a4d", "Operation Aquarius, p. 2"},
{ "5969f90786f77420d2328015", "Painkiller"},
{ "5969f9e986f7741dde183a50", "Pharmacist"},
{ "596a0e1686f7741ddf17dbee", "Supply plans"},
{ "596a101f86f7741ddb481582", "Kind of sabotage" },
{ "596a1e6c86f7741ddc2d3206", "General wares"},
{ "596a204686f774576d4c95de", "Sanitary Standards, p. 2"},
{ "596a218586f77420d232807c", "Car repair"},
{ "596b36c586f77450d6045ad2", "Supplier"},
{ "596b43fb86f77457ca186186", "The Extortionist"},
{ "596b455186f77457cb50eccb", "Stirrup"},
{ "5979ed3886f77431307dc512", "Whats on the flash drive?"},
{ "5979eee086f774311955e614", "Golden swag"},
{ "5979f8bb86f7743ec214c7a6", "Polikhim hobo"},
{ "5979f9ba86f7740f6c3fe9f2", "Chemical, p.1"},
{ "597a0b2986f77426d66c0633", "Chemical, p. 2"},
{ "597a0e5786f77426d66c0636", "Chemical, p. 3"},
{ "597a0f5686f774273b74f676", "Chemical, p. 4"},
{ "597a160786f77477531d39d2", "Out of curiosity"},
{ "597a171586f77405ba6887d3", "Big customer"},
{ "59c124d686f774189b3c843f", "BP depot"},
{ "59c50a9e86f7745fef66f4ff", "The Punisher"},
{ "59c50c8886f7745fed3193bf", "The Punisher. Part 2."},
{ "59c512ad86f7741f0d09de9b", "The Punisher. Part 3."},
{ "59c9392986f7742f6923add2", "Trust regain"},
{ "59c93e8e86f7742a406989c4", "Loyalty buyout"},
{ "59ca1a6286f774509a270942", "No offence"},
{ "59ca264786f77445a80ed044", "The Punisher. Part 4."},
{ "59ca29fb86f77445ab465c87", "The Punisher. Part 5."},
{ "59ca2eb686f77445a80ed049", "The Punisher. Part 6."},
{ "59f9da6786f774714230d751", ""},
{ "5a03153686f77442d90e2171", "Spa Tour. Part 1"},
{ "5a03173786f77451cb427172", "Spa Tour. Part 2"},
{ "5a0327ba86f77456b9154236", "Spa Tour. Part 3" },
{ "5a03296886f774569778596a", "Spa Tour. Part 4"},
{ "5a0449d586f77474e66227b7", "Spa Tour. Part 5"},
{ "5a27b75b86f7742e97191958", "Fishing Gear"},
{ "5a27b7a786f774579c3eb376", "Tigr Safari"},
{ "5a27b7d686f77460d847e6a6", " Scrap Metal"},
{ "5a27b80086f774429a5d7e20", "Eagle Eye"},
{ "5a27b87686f77460de0252a8", "Humanitarian Supplies"},
{ "5a27b9de86f77464e5044585", "The Cult. Part 1"},
{ "5a27ba1c86f77461ea5a3c56", "The Cult. Part 2"},
{ "5a27ba9586f7741b543d8e85", "Spa Tour. Part 6"},
{ "5a27bafb86f7741c73584017", "Spa Tour. Part 7"},
{ "5a27bb1e86f7741f27621b7e", "Cargo X. Part 1"},
{ "5a27bb3d86f77411ea361a21", "Cargo X. Part 2"},
{ "5a27bb5986f7741dfb660900", "Cargo X. Part 3"},
{ "5a27bb8386f7741c770d2d0a", "Wet Job. Part 1"},
{ "5a27bbf886f774333a418eeb", "Wet Job. Part 2"},
{ "5a27bc1586f7741f6d40fa2f", "Wet Job. Part 3"},
{ "5a27bc3686f7741c73584026", "Wet Job. Part 4"},
{ "5a27bc6986f7741c7358402b", "Wet Job. Part 5"},
{ "5a27bc8586f7741b543d8ea4", "Wet Job. Part 6"},
{ "5a27c99a86f7747d2c6bdd8e", "Friend from the West. Part 1"},
{ "5a27d2af86f7744e1115b323", "Friend from the West. Part 2"},
{ "5a5642ce86f77445c63c3419", "Hippocratic Vow"},
{ "5a68661a86f774500f48afb0", "Health Care Privacy. Part 1"},
{ "5a68663e86f774501078f78a", "Health Care Privacy. Part 2"},
{ "5a68665c86f774255929b4c7", "Health Care Privacy. Part 3"},
{ "5a68667486f7742607157d28", "Health Care Privacy. Part 4"},
{ "5a68669a86f774255929b4d4", "Health Care Privacy. Part 5"},
{ "5ac23c6186f7741247042bad", "Gunsmith. Part 1"},
{ "5ac2426c86f774138762edfe", "Gunsmith. Part 2"},
{ "5ac2428686f77412450b42bf", "Gunsmith. Part 3"},
{ "5ac242ab86f77412464f68b4", "Gunsmith. Part 5"},
{ "5ac244c486f77413e12cf945", "Gunsmith. Part 6"},
{ "5ac244eb86f7741356335af1", "Gunsmith. Part 4"},
{ "5ac345dc86f774288030817f", "Farming. Part 1" },
{ "5ac3460c86f7742880308185", "Farming. Part 2" },
{ "5ac3462b86f7741d6118b983", "Farming. Part 3" },
{ "5ac3464c86f7741d651d6877", "Farming. Part 4" },
{ "5ac3467986f7741d6224abc2", "Signal. Part 1" },
{ "5ac346a886f7744e1b083d67", "Signal. Part 2" },
{ "5ac346cf86f7741d63233a02", "Signal. Part 3" },
{ "5ac346e886f7741d6118b99b", "Signal. Part 4" },
{ "5ac3475486f7741d6224abd3", "Bad habit" },
{ "5ac3477486f7741d651d6885", "Scout" },
{ "5ac3479086f7742880308199", "Insider" },
{ "5ae3267986f7742a413592fe", "Gunsmith. Part 7" },
{ "5ae3270f86f77445ba41d4dd", "Gunsmith. Part 8" },
{ "5ae3277186f7745973054106", "Gunsmith. Part 9" },
{ "5ae327c886f7745c7b3f2f3f", "Gunsmith. Part 10" },
{ "5ae3280386f7742a41359364", "Gunsmith. Part 11" },
{ "5ae448a386f7744d3730fff0", "Only business" },
{ "5ae448bf86f7744d733e55ee", "Make ULTRA Great Again" },
{ "5ae448e586f7744dcf0c2a67", "Big sale" },
{ "5ae448f286f77448d73c0131", "The Blood of War" },
{ "5ae4490786f7744ca822adcc", "Dressed to kill" },
{ "5ae4493486f7744efa289417", "Database. Part 1" },
{ "5ae4493d86f7744b8e15aa8f", "Database. Part 2" },
{ "5ae4495086f77443c122bc40", "Sew it good. Part 1" },
{ "5ae4495c86f7744e87761355", "Sew it good. Part 2" },
{ "5ae4496986f774459e77beb6", "Sew it good. Part 3" },
{ "5ae4497b86f7744cf402ed00", "Sew it good. Part 4" },
{ "5ae4498786f7744bde357695", "The key to success" },
{ "5ae4499a86f77449783815db", "Charisma brings success" },
{ "5ae449a586f7744bde357696", "No fuss needed" },
{ "5ae449b386f77446d8741719", "Gratitude" },
{ "5ae449c386f7744bde357697", "Sales Night" },
{ "5ae449d986f774453a54a7e1", "Supervisor" },
{ "5b47749f86f7746c5d6a5fd4", "Gunsmith. Part 12" },
{ "5b47799d86f7746c5d6a5fd8", "Gunsmith. Part 13" },
{ "5b477b6f86f7747290681823", "Gunsmith. Part 14" },
{ "5b477f7686f7744d1b23c4d2", "Gunsmith. Part 15" },
{ "5b47825886f77468074618d3", "Gunsmith. Part 16" },
{ "5b47876e86f7744d1c353205", "The Blood of War Part 2" },
{ "5b47891f86f7744d1b23c571", "Living high is not a crime" },
{ "5b478b1886f7744d1b23c57d", "Hot delivery" },
{ "5b478d0f86f7744d190d91b5", "Minibus" },
{ "5b478eca86f7744642012254", "\"Vitamins\" p.1" },
{ "5b478ff486f7744d184ecbbf", "\"Vitamins\" p. 2" },
{ "5b47926a86f7747ccc057c15", "Informed means armed" },
{ "5b4794cb86f774598100d5d4", "Lend lease" },
{ "5b4795fb86f7745876267770", "Chumming" },
{ "5bc4776586f774512d07cf05", "\"The Tarkov shooter\" Part 1"},
{ "5bc479e586f7747f376c7da3", "\"The Tarkov shooter\" Part 2"},
{ "5bc47dbf86f7741ee74e93b9", "\"The Tarkov shooter\". Part 3" },
{ "5bc480a686f7741af0342e29", "\"The Tarkov shooter\". Part 4" },
{ "5bc4826c86f774106d22d88b", "\"The Tarkov shooter\". Part 5" },
{ "5bc4836986f7740c0152911c", "\"The Tarkov shooter\". Part 6" },
{ "5bc4856986f77454c317bea7", "\"The Tarkov shooter\". Part 7" },
{ "5bc4893c86f774626f5ebf3e", "\"The Tarkov shooter\". Part 8" },
{ "5c0bbaa886f7746941031d82", "Bullshit" },
{ "5c0bc91486f7746ab41857a2", "Silent caliber" },
{ "5c0bd01e86f7747cdd799e56", "Insomnia" },
{ "5c0bd94186f7747a727f09b2", "Test drive. Pt. 1" },
{ "5c0bdb5286f774166e38eed4", "Flint" },
{ "5c0bde0986f77479cf22c2f8", "A Shooter Born in Heaven" },
{ "5c0be13186f7746f016734aa", "Psycho Sniper" },
{ "5c0be5fc86f774467a116593", "Private clinic" },
{ "5c0d0d5086f774363760aef2", "Athlete" },
{ "5c0d0f1886f77457b8210226", "Lend lease. Part 2" },
{ "5c0d190cd09282029f5390d8", "Grenadier" },
{ "5c0d1c4cd0928202a02a6f5c", "Decontamination service" },
{ "5c0d4c12d09282029f539173", "Peacekeeping mission" },
{ "5c0d4e61d09282029f53920e", "The guide" },
{ "5c10f94386f774227172c572", "The Blood of War Pt. 3" },
{ "5c1128e386f7746565181106", "Fertilizers" },
{ "5c112d7e86f7740d6f647486", "Scavenger" },
{ "5c1141f386f77430ff393792", "Living high is not a crime Pt.2" },
{ "5c1234c286f77406fa13baeb", "Setup" },
{ "5c12452c86f7744b83469073", "Perfect mediator" },
{ "5c139eb686f7747878361a6f", "Import" },
{ "5c51aac186f77432ea65c552", "Collector" },
{ "5d2495a886f77425cd51e403", "Introduction" },
{ "5d24b81486f77439c92d6ba8", "Acquaintance" },
{ "5d25aed386f77442734d25d2", "The survivalist path. Unprotected, but dangerous"},
{ "5d25b6be86f77444001e1b89", "The survivalist path. Thrifty" },
{ "5d25bfd086f77442734d3007", "The survivalist path. Zhivchik" },
{ "5d25c81b86f77443e625dd71", "The survivalist path. Wounded beast" },
{ "5d25cf2686f77443e75488d4", "The survivalist path. Tough guy" },
{ "5d25d2c186f77443e35162e5", "The survivalist path. Cold blooded" },
{ "5d25dae186f77443e55d2f78", "The survivalist path. Zatoichi" },
{ "5d25e29d86f7740a22516326", "The survivalist path. Eagle-owl" },
{ "5d25e2a986f77409dd5cdf2a", "The survivalist path. Combat medic" },
{ "5eaaaa7c93afa0558f3b5a1c", "The survivalist path. Junkie" },
{ "5d25e2b486f77409de05bba0", "Huntsman path. Secured perimeter" },
{ "5d25e2c386f77443e7549029", "Huntsman path. The trophy" },
{ "5d25e2cc86f77443e47ae019", "Huntsman path. Woods cleaning" },
{ "5d25e2d886f77442734d335e", "Huntsman path. Controller" },
{ "5d25e2e286f77444001e2e48", "Huntsman path. Sell-out" },
{ "5d25e2ee86f77443e35162ea", "Huntsman path. Woods keeper" },
{ "5d25e43786f7740a212217fa", "Huntsman path. Justice" },
{ "5d25e44386f77409453bce7b", "Huntsman path. Evil watchman" },
{ "5d25e44f86f77443e625e385", "Huntsman path. Eraser" },
{ "5d25e45e86f77408251c4bfa", "Huntsman path. Eraser pt. 2" },
{ "5d25e46e86f77409453bce7c", "Ambulance" },
{ "5d25e48186f77443e625e386", "Courtesy visit" },
{ "5d25e48d86f77408251c4bfb", "Shady business" },
{ "5d25e4ad86f77443e625e387", "Nostalgia" },
{ "5d25e4b786f77408251c4bfc", "Fishing place" },
{ "5d25e4ca86f77409dd5cdf2c", "Hunting trip" },
{ "5d25e4d586f77443e625e388", "Reserv" },
{ "5d4bec3486f7743cac246665", "Regulated materials" },
{ "5d6fb2c086f77449da599c24", "An apple a day - keeps the doctor away" },
{ "5d6fbc2886f77449d825f9d3", "Mentor" },
{ "5dc53acb86f77469c740c893", "The stylish one" },
{ "5e381b0286f77420e3417a74", "Textile. Part 1" },
{ "5e4d515e86f77438b2195244", "Textile. Part 2" },
{ "5ede55112c95834b583f052a", "Bunker. Part 1" },
{ "5ede567cfa6dc072ce15d6e3", "Bunker. Part 2" },
{ "5eda19f0edce541157209cee", "Anesthesia" },
{ "5edab4b1218d181e29451435", "Huntsman path. Sadist" },
{ "5edab736cc183c769d778bc2", "Colleagues. Pt.1" },
{ "5edaba7c0c502106f869bc02", "Colleagues. Pt.2" },
{ "5edac34d0bb72a50635c2bfa", "Colleagues. Pt.3" },
{ "5edabd13218d181e29451442", "Rigged game" },
{ "5edac020218d181e29451446", "Samples" },
{ "5edac63b930f5454f51e128b", "TerraGroup employee" },
{ "5f04886a3937dc337a6b8238", "The chemistry closet" },
{ "5fd9fad9c1ce6b1a3b486d00", "Search Mission" },
{ "600302d73b897b11364cd161", "Hunter" },
{ "6086c852c945025d41566124", "Revision" },
{ "608a768d82e40b3c727fd17d", "Pest control" },
{ "6089736efa70fc097863b8f6", "Back door" },
{ "6089743983426423753cd58a", "Safe corridor" },
{ "608974af4b05530f55550c21", "Inventory check" },
{ "608974d01a66564e74191fc0", "Fuel matter" },
{ "60896bca6ee58f38c417d4f2", "No place for renegades" },
{ "60896e28e4a85c72ef3fa301", "Disease history" },
{ "60896b7bfa70fc097863b8f5", "Documents" },
{ "6089732b59b92115597ad789", "Surplus goods" },
{ "60896888e4a85c72ef3fa300", "Experience exchange" },
{ "60c0c018f7afb4354815096a", "Tagilla"},
{ "60e71dc0a94be721b065bbfc", "Intimidator" },
{ "60e71dc67fcf9c556f325056", "level 55 quest" },
{ "60e71e8ed54b755a3b53eb67", "Huntsman Path - Relentless" },
{ "60e729cf5698ee7b05057439", "Swift One" },
{ "60effd818b669d08a35bfad5", "The Choice" }
};
private static QuestRoot _liveQuestData;
private static Dictionary<string, Quest> _questData;
private static List<AssortUnlocks> _assortUnlocks;
public static Dictionary<string, Quest> GetQuestData(string filename = "quests")
{
if (_questData == null)
{
var questFilePath = InputFileHelper.GetInputFilePaths().FirstOrDefault(x => x.Contains(filename));
var questDataJson = File.ReadAllText(questFilePath);
_questData = JsonSerializer.Deserialize<Dictionary<string, Quest>>(questDataJson);
}
return _questData;
}
public static QuestRoot GetLiveQuestData(string filename = "resp.client.quest.list")
{
if (_liveQuestData == null)
{
var questFilePath = InputFileHelper.GetInputFilePaths().FirstOrDefault(x => x.Contains(filename));
var questDataJson = File.ReadAllText(questFilePath);
_liveQuestData = JsonSerializer.Deserialize<QuestRoot>(questDataJson);
}
return _liveQuestData;
}
public static string GetQuestNameById(string id)
{
if (!questNames.ContainsKey(id))
{
return "Unknown quest";
}
return questNames[id];
}
public static List<AssortUnlocks> GetAssortUnlocks()
{
if (_assortUnlocks == null)
{
_assortUnlocks = new List<AssortUnlocks>();
foreach (var quest in GetQuestData())
{
foreach (var reward in quest.Value.rewards.Success)
{
if (string.Equals(reward.type, "assortmentunlock", System.StringComparison.OrdinalIgnoreCase))
{
_assortUnlocks.Add(new AssortUnlocks
{
AssortUnlockId = reward.id,
ItemUnlockedId = reward.target,
ItemUnlockedTemplateId = reward.items[0]._tpl,
LoyaltyLevel = (int)reward.loyaltyLevel,
QuestId = quest.Key,
QuestRewardId = reward.id,
TraderId = reward.traderId,
TraderType = TraderHelper.GetTraderTypeById(reward.traderId),
Criteria = "Success"
}
);
}
}
}
}
return _assortUnlocks;
}
}
}

View File

@ -0,0 +1,44 @@
using QuestValidator.Models.Other;
using System.Collections.Generic;
namespace AssortGenerator.Common.Helpers
{
public static class TraderHelper
{
private static Dictionary<Trader, string> _traders;
public static Dictionary<Trader, string> GetTraders()
{
if (_traders == null)
{
_traders = new Dictionary<Trader, string>
{
{ Trader.Prapor, "54cb50c76803fa8b248b4571" },
{ Trader.Therapist, "54cb57776803fa99248b456e"},
{ Trader.Skier, "58330581ace78e27b8b10cee"},
{ Trader.Peacekeeper, "5935c25fb3acc3127c3d8cd9"},
{ Trader.Mechanic, "5a7c2eca46aef81a7ca2145d"},
{ Trader.Ragman, "5ac3b934156ae10c4430e83c"},
{ Trader.Jaeger, "5c0647fdd443bc2504c2d371"}
};
}
return _traders;
}
public static Trader GetTraderTypeById(string traderId)
{
Trader returnType = 0;
foreach (var item in GetTraders())
{
if (item.Value == traderId)
{
returnType = item.Key;
break;
}
}
return returnType;
}
}
}

View File

@ -0,0 +1,55 @@
using System.Collections.Generic;
namespace QuestValidator.Common.Models
{
public class ItemsLibRoot
{
public List<Dictionary<string, Item>> items { get; set; }
}
public class Item
{
public string _id { get; set; }
public string _name { get; set; }
public string _parent { get; set; }
public string _type { get; set; }
public Props _props { get; set; }
}
public class Props
{
public string Name { get; set; }
public string ShortName { get; set; }
public string Description { get; set; }
public List<Chamber> Chambers { get; set; }
public List<Slot> Slots { get; set; }
public string defAmmo { get; set; }
}
public class Chamber
{
public string _name { get; set; }
public string _id { get; set; }
public string _parent { get; set; }
public ChamberProps _props { get; set; }
public bool _required { get; set; }
public bool _mergeSlotWithChildren { get; set; }
public string _proto { get; set; }
}
public class Slot
{
public string _name { get; set; }
public bool _required { get; set; }
}
public class ChamberProps
{
public List<Filter> filters { get; set; }
}
public class Filter
{
public List<string> filter { get; set; }
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\QuestValidator.Models\QuestValidator.Models.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Assets\items.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,113 @@
using System.Collections.Generic;
namespace QuestValidator.Models
{
public class QuestRoot
{
public int err { get; set; }
public object errmsg { get; set; }
public List<Quest> data { get; set; }
}
public class Quest
{
public string _id { get; set; }
public bool canShowNotificationsInGame { get; set; }
public Conditions conditions { get; set; }
public string traderId { get; set; }
public string location { get; set; }
public string image { get; set; }
public string type { get; set; }
public bool isKey { get; set; }
public bool restartable { get; set; }
public bool instantComplete { get; set; }
public bool secretQuest { get; set; }
public int min_level { get; set; }
public Rewards rewards { get; set; }
}
public class Conditions
{
public string _parent { get; set; }
public ConditionProps _props { get; set; }
public List<AvailableFor> AvailableForFinish { get; set; }
public List<AvailableFor> AvailableForStart { get; set; }
//public List<object> Fail { get; set; }
}
public class ConditionProps
{
public string compareMethod { get; set; }
public string id { get; set; }
public object target { get; set; }
public string value { get; set; }
public Counter counter { get; set; }
public int index { get; set; }
public string parentId { get; set; }
public string type { get; set; }
public int? dogtagLevel { get; set; }
public int? maxDurability { get; set; }
public int? minDurability { get; set; }
public List<object> visibilityConditions { get; set; }
public List<int> status { get; set; }
}
public class Counter
{
//public List<Condition> conditions { get; set; }
public string id { get; set; }
}
public class AvailableFor
{
public string _parent { get; set; }
public AvailableForProps _props { get; set; }
}
public class AvailableForProps
{
public int dogtagLevel { get; set; }
public string id { get; set; }
public int? index { get; set; }
public int? maxDurability { get; set; }
public int? minDurability { get; set; }
public string parentId { get; set; }
public bool resetOnSessionEnd { get; set; }
public object target { get; set; }
//public int value { get; set; }
public List<object> visibilityConditions { get; set; }
}
public class Rewards
{
public List<RewardStatus> Started { get; set; }
public List<RewardStatus> Success { get; set; }
public List<RewardStatus> Fail { get; set; }
}
public class RewardStatus
{
public string value { get; set; }
public string id { get; set; }
public string type { get; set; }
public int index { get; set; }
public string target { get; set; }
public List<QuestRewardItem> items { get; set; }
public int? loyaltyLevel { get; set; }
public string traderId { get; set; }
}
public class QuestRewardItem
{
public string _id { get; set; }
public string _tpl { get; set; }
public QuestRewardUpd upd { get; set; }
public string parentId { get; set; }
public string slotId { get; set; }
}
public class QuestRewardUpd
{
public int StackObjectsCount { get; set; }
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace QuestValidator.Models
{
public class Items
{
public string _id { get; set; }
public string _tpl { get; set; }
public string parentId { get; set; }
public string slotId { get; set; }
public TraderUpd upd { get; set; }
}
public class TraderUpd
{
public bool UnlimitedCount { get; set; }
public int StackObjectsCount { get; set; }
public int? BuyRestrictionMax { get; set; }
public int? BuyRestrictionCurrent { get; set; }
}
public class BarterScheme
{
}
public class LoyaltyLevelItems
{
Dictionary<string, int> items { get; set;}
}
}

View File

@ -0,0 +1,15 @@
namespace QuestValidator.Models.Other
{
public class AssortUnlocks
{
public string QuestId { get; set; }
public string AssortUnlockId { get; set; }
public string QuestRewardId { get; set; }
public string TraderId { get; set; }
public string ItemUnlockedId { get; set; }
public string ItemUnlockedTemplateId { get; set; }
public Trader TraderType { get; set; }
public int LoyaltyLevel { get; set; }
public string Criteria { get; set; }
}
}

View File

@ -0,0 +1,14 @@
namespace QuestValidator.Models.Other
{
public enum Trader
{
Unknown = 0,
Prapor = 1,
Therapist = 2,
Skier = 3,
Peacekeeper = 4,
Mechanic = 5,
Ragman = 6,
Jaeger = 7
}
}

View File

@ -0,0 +1,34 @@
using System.Collections.Generic;
namespace QuestValidator.Models.Output
{
public class AssortRoot
{
public List<Item> items { get; set; }
public Dictionary<string, object> barter_scheme { get; set; }
public Dictionary<string, int> loyal_level_items { get; set; }
}
public class Item
{
public string _id { get; set; }
public string _tpl { get; set; }
public string parentId { get; set; }
public string slotId { get; set; }
public Upd upd { get; set; }
}
public class Upd
{
public bool UnlimitedCount { get; set; }
public int StackObjectsCount { get; set; }
public int? BuyRestrictionMax { get; set; }
public int? BuyRestrictionCurrent { get; set; }
}
public class BarterObject
{
public int count { get; set; }
public string _tpl { get; set; }
}
}

View File

@ -0,0 +1,67 @@
using System.Collections.Generic;
namespace QuestValidator.Models.Output
{
public class BaseRoot
{
public List<Base> data { get; set; }
}
public class Base
{
public string _id { get; set; }
public bool customization_seller { get; set; }
public string name { get; set; }
public string surname { get; set; }
public string nickname { get; set; }
public string location { get; set; }
public string avatar { get; set; }
public int balance_rub { get; set; }
public int balance_dol { get; set; }
public int balance_eur { get; set; }
public bool unlockedByDefault { get; set; }
public int discount { get; set; }
public int discount_end { get; set; }
public bool buyer_up { get; set; }
public string currency { get; set; }
public int nextResupply { get; set; }
public Repair repair { get; set; }
public Insurance insurance { get; set; }
public bool medic { get; set; }
public int gridHeight { get; set; }
public List<LoyaltyLevel> loyaltyLevels { get; set; }
public List<string> sell_category { get; set; }
}
public class Repair
{
public bool availability { get; set; }
public string quality { get; set; }
public List<object> excluded_id_list { get; set; }
public List<object> excluded_category { get; set; }
public string currency { get; set; }
public int currency_coefficient { get; set; }
public int price_rate { get; set; }
}
public class Insurance
{
public bool availability { get; set; }
public int min_payment { get; set; }
public int min_return_hour { get; set; }
public int max_return_hour { get; set; }
public int max_storage_time { get; set; }
public List<object> excluded_category { get; set; }
}
public class LoyaltyLevel
{
public object minLevel { get; set; }
public object minSalesSum { get; set; }
public object minStanding { get; set; }
public int buy_price_coef { get; set; }
public object repair_price_coef { get; set; }
public object insurance_price_coef { get; set; }
public int exchange_price_coef { get; set; }
public object heal_price_coef { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace QuestValidator.Models.Output
{
public class QuestAssort
{
public QuestAssort()
{
started = new Dictionary<string, string>();
success = new Dictionary<string, string>();
fail = new Dictionary<string, string>();
}
public Dictionary<string, string> started { get; set; }
public Dictionary<string, string> success { get; set; }
public Dictionary<string, string> fail { get; set; }
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace QuestValidator.Models.Input
{
public class Suit
{
public string _id { get; set; }
public string tid { get; set; }
public string suiteId { get; set; }
public bool isActive { get; set; }
public Requirements requirements { get; set; }
}
public class Requirements
{
public int loyaltyLevel { get; set; }
public int profileLevel { get; set; }
public int standing { get; set; }
public List<object> skillRequirements { get; set; }
public List<string> questRequirements { get; set; }
public List<ItemRequirement> itemRequirements { get; set; }
}
public class ItemRequirement
{
public int count { get; set; }
public string _tpl { get; set; }
public bool onlyFunctional { get; set; }
}
}

View File

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
</Project>

37
QuestValidator.sln Normal file
View File

@ -0,0 +1,37 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31624.102
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestValidator", "QuestValidator\QuestValidator.csproj", "{52D43AB5-26C2-4C82-82F2-F63ED911E7F4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestValidator.Common", "QuestValidator.Common\QuestValidator.Common.csproj", "{3F753E26-069F-43EB-979C-B89EE07AB658}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestValidator.Models", "QuestValidator.Models\QuestValidator.Models.csproj", "{5897813D-96DB-42E2-A436-0CF4472F9C40}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{52D43AB5-26C2-4C82-82F2-F63ED911E7F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{52D43AB5-26C2-4C82-82F2-F63ED911E7F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52D43AB5-26C2-4C82-82F2-F63ED911E7F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52D43AB5-26C2-4C82-82F2-F63ED911E7F4}.Release|Any CPU.Build.0 = Release|Any CPU
{3F753E26-069F-43EB-979C-B89EE07AB658}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F753E26-069F-43EB-979C-B89EE07AB658}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F753E26-069F-43EB-979C-B89EE07AB658}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F753E26-069F-43EB-979C-B89EE07AB658}.Release|Any CPU.Build.0 = Release|Any CPU
{5897813D-96DB-42E2-A436-0CF4472F9C40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5897813D-96DB-42E2-A436-0CF4472F9C40}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5897813D-96DB-42E2-A436-0CF4472F9C40}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5897813D-96DB-42E2-A436-0CF4472F9C40}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DDDE8A01-15DB-45C5-A701-77A6643E3396}
EndGlobalSection
EndGlobal

263
QuestValidator/Program.cs Normal file
View File

@ -0,0 +1,263 @@
using AssortGenerator.Common.Helpers;
using QuestValidator.Common;
using QuestValidator.Common.Helpers;
using QuestValidator.Helpers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace QuestValidator
{
class Program
{
static void Main(string[] args)
{
var inputPath = CreateWorkingFolders();
InputFileHelper.SetInputFiles(inputPath);
//read in quest file
var questData = QuestHelper.GetQuestData();
var liveQuestData = QuestHelper.GetLiveQuestData();
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 ?", 0))
{
LoggingHelpers.LogInfo("");
continue;
}
CheckRootItemValues(quest, relatedLiveQuest);
CheckSuccessRewardItems(quest, relatedLiveQuest);
CheckAvailableForFinishConditionItems(quest, relatedLiveQuest);
LoggingHelpers.LogInfo("");
LoggingHelpers.LogInfo("-----");
LoggingHelpers.LogInfo("");
}
}
private static void CheckAvailableForFinishConditionItems(Models.Quest quest, Models.Quest relatedLiveQuest)
{
foreach (var availableForFinishItem in quest.conditions.AvailableForFinish)
{
Models.AvailableFor liveFinishItem = relatedLiveQuest.conditions.AvailableForFinish.Find(x => x._props.id == availableForFinishItem._props.id);
if (!ItemExists(liveFinishItem, "AvailableForFinish item", availableForFinishItem._props.index.Value))
{
continue;
}
CheckValuesMatch(availableForFinishItem._props.parentId, liveFinishItem._props.parentId, "AvailableForFinish parentId mismatch", availableForFinishItem._props.id);
// check reset on session end
CheckValuesMatch(availableForFinishItem._props.resetOnSessionEnd, liveFinishItem._props.resetOnSessionEnd, "AvailableForFinish resetOnSessionEnd value mismatch", availableForFinishItem._props.id);
}
}
private static void CheckSuccessRewardItems(Models.Quest quest, Models.Quest relatedLiveQuest)
{
var liveQuestSuccessRewardItems = relatedLiveQuest.rewards.Success;
foreach (var questSuccessRewardItem in quest.rewards.Success.Where(x => x.type == "Item"))
{
var errorMessage = string.Empty;
// Get live reward item by index and type
var relatedLiveRewardItem = liveQuestSuccessRewardItems.Find(x => x.index == questSuccessRewardItem.index && x.type == "Item");
if (relatedLiveRewardItem == null)
{
// Get live reward item by templateId and type as we cant find it by index
relatedLiveRewardItem = liveQuestSuccessRewardItems.Find(x => x.items != null && x.items[0]?._tpl == questSuccessRewardItem.items[0]?._tpl && x.type == "Item");
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}");
LoggingHelpers.LogError($"ERROR Skipping quest success item: {questSuccessRewardItem.id}");
continue;
}
}
//LoggingHelpers.LogInfo($"INFO Found live success reward item: {relatedLiveRewardItem.id}");
// 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, relatedLiveRewardItem.items[0].upd.StackObjectsCount, "mismatch for StackObjectsCount", questSuccessRewardItem.items[0]._id);
}
}
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");
}
}
private static void CheckRootItemValues(Models.Quest quest, Models.Quest relatedLiveQuest)
{
// Check image id matches
CheckValuesMatch(quest.image.Substring(0, quest.image.Length - 3), relatedLiveQuest?.image.Substring(0, relatedLiveQuest.image.Length - 3), "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");
}
private static void LogQuestDetails(Models.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 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(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(Models.QuestRoot liveQuestData, Dictionary<string, Models.Quest> akiQuestData)
{
var missingQuests = new List<string>();
foreach (var liveQuest in liveQuestData.data)
{
if (!akiQuestData.ContainsKey(liveQuest._id))
{
missingQuests.Add($"{liveQuest._id} {QuestHelper.GetQuestNameById(liveQuest._id)}");
}
}
if (missingQuests.Count > 0)
{
LoggingHelpers.LogWarning($"WARNING aki quest list is missing quests:");
foreach (var item in missingQuests)
{
LoggingHelpers.LogWarning(item);
}
}
}
private static string CreateWorkingFolders()
{
var workingPath = Directory.GetCurrentDirectory();
// create input folder
var inputPath = $"{workingPath}//input";
DiskHelpers.CreateDirIfDoesntExist(inputPath);
return inputPath;
}
}
}

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\QuestValidator.Common\QuestValidator.Common.csproj" />
<ProjectReference Include="..\QuestValidator.Models\QuestValidator.Models.csproj" />
</ItemGroup>
</Project>

View File

@ -1,3 +1,7 @@
# ChompQuestVerifier
Verifies an aki quest json against a live dump of quest data
Verifies an aki quest json against a live dump of quest data
## How to use
Copy your aki quests.json into QuestValidator\bin\Debug\netcoreapp3.1\input
Copy a live quest file dump (resp.client.quest.list_xxxx) into QuestValidator\bin\Debug\netcoreapp3.1\input