From 5ca759a7568165bea4b3c31c9ef55993eac0d6b5 Mon Sep 17 00:00:00 2001 From: Noah Pilarski Date: Wed, 11 Sep 2024 08:08:06 -0400 Subject: [PATCH] shop data --- Winch/Core/AssetLoader.cs | 18 +++ Winch/Data/Shop/ModdedShopData.cs | 39 +++++ Winch/Patches/API/ShopLoadPatcher.cs | 20 +++ Winch/Serialization/DredgeTypeHelpers.cs | 58 ++++++++ .../Shop/DialogueLinkedShopDataConverter.cs | 21 +++ .../Shop/PhaseLinkedShopDataConverter.cs | 19 +++ Winch/Serialization/Shop/ShopDataConverter.cs | 25 ++++ .../Shop/ShopItemDataConverter.cs | 19 +++ Winch/Util/ShopUtil.cs | 136 ++++++++++++++++++ 9 files changed, 355 insertions(+) create mode 100644 Winch/Data/Shop/ModdedShopData.cs create mode 100644 Winch/Patches/API/ShopLoadPatcher.cs create mode 100644 Winch/Serialization/Shop/DialogueLinkedShopDataConverter.cs create mode 100644 Winch/Serialization/Shop/PhaseLinkedShopDataConverter.cs create mode 100644 Winch/Serialization/Shop/ShopDataConverter.cs create mode 100644 Winch/Serialization/Shop/ShopItemDataConverter.cs create mode 100644 Winch/Util/ShopUtil.cs diff --git a/Winch/Core/AssetLoader.cs b/Winch/Core/AssetLoader.cs index 2bc1b5fb..8aa1a288 100644 --- a/Winch/Core/AssetLoader.cs +++ b/Winch/Core/AssetLoader.cs @@ -62,6 +62,7 @@ private static void LoadAssetFolder(string path) string harvestZoneFolderpath = Path.Combine(path, "HarvestZones"); string vibrationFolderpath = Path.Combine(path, "Vibrations"); string mapMarkerFolderpath = Path.Combine(path, "MapMarkers"); + string shopFolderpath = Path.Combine(path, "Shops"); string dockFolderpath = Path.Combine(path, "Docks"); string abilityFolderpath = Path.Combine(path, "Abilities"); string worldEventFolderpath = Path.Combine(path, "WorldEvents"); @@ -76,6 +77,7 @@ private static void LoadAssetFolder(string path) if(Directory.Exists(itemFolderPath)) LoadItemFiles(itemFolderPath); if(Directory.Exists(vibrationFolderpath)) LoadVibrationFiles(vibrationFolderpath); if(Directory.Exists(mapMarkerFolderpath)) LoadMapMarkerFiles(mapMarkerFolderpath); + if(Directory.Exists(shopFolderpath)) LoadShopFiles(shopFolderpath); if(Directory.Exists(dockFolderpath)) LoadDockFiles(dockFolderpath); if(Directory.Exists(poiFolderpath)) LoadPoiFiles(poiFolderpath); if(Directory.Exists(harvestZoneFolderpath)) LoadHarvestZoneFiles(harvestZoneFolderpath); @@ -228,6 +230,22 @@ private static void LoadMapMarkerFiles(string mapMarkerFolderPath) } } + private static void LoadShopFiles(string shopFolderPath) + { + string[] shopFolderFiles = Directory.GetFiles(shopFolderPath); + foreach (string file in shopFolderFiles) + { + try + { + ShopUtil.AddShopDataFromMeta(file); + } + catch (Exception ex) + { + WinchCore.Log.Error($"Failed to load shop data from {file}: {ex}"); + } + } + } + private static void LoadPoiFilesOfType(string poiFolderPath) where T : CustomPOI { string[] poiFiles = Directory.GetFiles(poiFolderPath); diff --git a/Winch/Data/Shop/ModdedShopData.cs b/Winch/Data/Shop/ModdedShopData.cs new file mode 100644 index 00000000..1af1041b --- /dev/null +++ b/Winch/Data/Shop/ModdedShopData.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq; +using Winch.Util; + +namespace Winch.Data.Shop; + +public class ModdedShopData : ShopData +{ + public string id = string.Empty; + + public GridKey gridKey = GridKey.NONE; + + internal void Populate() + { + foreach (var itemData in alwaysInStock.Concat(phaseLinkedShopData.SelectMany(pl => pl.itemData)).Concat(dialogueLinkedShopData.SelectMany(pl => pl.itemData))) + { + if (itemData is ModdedShopItemData moddedItemData) + moddedItemData.Populate(); + } + } + + public static implicit operator ShopRestocker.ShopDataGridConfig(ModdedShopData shopData) => new ShopRestocker.ShopDataGridConfig + { + shopData = shopData, + gridKey = shopData.gridKey, + }; + + public class ModdedShopItemData : ShopItemData + { + public new string itemData = string.Empty; + + public SpatialItemData ItemData => ItemUtil.GetSpatialItemData(itemData); + + internal void Populate() + { + base.itemData = ItemData; + } + } +} diff --git a/Winch/Patches/API/ShopLoadPatcher.cs b/Winch/Patches/API/ShopLoadPatcher.cs new file mode 100644 index 00000000..abf3d6e2 --- /dev/null +++ b/Winch/Patches/API/ShopLoadPatcher.cs @@ -0,0 +1,20 @@ +using HarmonyLib; +using System.Collections.Generic; +using System.Linq; +using UnityEngine.ResourceManagement.AsyncOperations; +using Winch.Core.API; +using Winch.Util; +using static ShopRestocker; + +namespace Winch.Patches.API; + +[HarmonyPatch(typeof(ShopRestocker))] +[HarmonyPatch(nameof(ShopRestocker.Awake))] +internal static class ShopLoadPatcher +{ + public static void Postfix(ShopRestocker __instance) + { + ShopUtil.AddModdedShopData(__instance); + ShopUtil.PopulateShopData(__instance.shopDataGridConfigs); + } +} diff --git a/Winch/Serialization/DredgeTypeHelpers.cs b/Winch/Serialization/DredgeTypeHelpers.cs index fe8d4e6a..ef96777d 100644 --- a/Winch/Serialization/DredgeTypeHelpers.cs +++ b/Winch/Serialization/DredgeTypeHelpers.cs @@ -11,9 +11,11 @@ using Winch.Data.Item.Prerequisites; using Winch.Data.POI.Dock; using Winch.Data.POI.Dock.Destinations; +using Winch.Data.Shop; using Winch.Data.WorldEvent.Condition; using Winch.Serialization.Vibration; using Winch.Util; +using static ShopData; namespace Winch.Serialization; @@ -735,4 +737,60 @@ public static List ParseUpgradeDestinations(JArray des } return parsed; } + + public static ModdedShopData.ModdedShopItemData ParseShopItemData(JToken shopItemData) + { + var config = new ModdedShopData.ModdedShopItemData(); + var meta = shopItemData.Type == JTokenType.String + ? new Dictionary { { "itemData", shopItemData.ToString() } } + : shopItemData.ToObject>() ?? new Dictionary(); + ShopUtil.PopulateShopItemDataFromMetaWithConverter(config, meta); + return config; + } + + internal static List ParseShopItemDataList(JArray o) + { + var parsed = new List(); + foreach (var shopItemData in o) + { + parsed.Add(ParseShopItemData(shopItemData)); + } + return parsed; + } + + public static PhaseLinkedShopData ParsePhaseLinkedShopData(JToken shopData) + { + var config = new PhaseLinkedShopData(); + var meta = shopData.ToObject>() ?? new Dictionary(); + ShopUtil.PopulatePhaseLinkedShopDataFromMetaWithConverter(config, meta); + return config; + } + + internal static List ParsePhaseLinkedShopDataList(JArray o) + { + var parsed = new List(); + foreach (var shopItemData in o) + { + parsed.Add(ParsePhaseLinkedShopData(shopItemData)); + } + return parsed; + } + + public static DialogueLinkedShopData ParseDialogueLinkedShopData(JToken shopData) + { + var config = new DialogueLinkedShopData(); + var meta = shopData.ToObject>() ?? new Dictionary(); + ShopUtil.PopulateDialogueLinkedShopDataFromMetaWithConverter(config, meta); + return config; + } + + internal static List ParseDialogueLinkedShopDataList(JArray o) + { + var parsed = new List(); + foreach (var shopItemData in o) + { + parsed.Add(ParseDialogueLinkedShopData(shopItemData)); + } + return parsed; + } } diff --git a/Winch/Serialization/Shop/DialogueLinkedShopDataConverter.cs b/Winch/Serialization/Shop/DialogueLinkedShopDataConverter.cs new file mode 100644 index 00000000..94d64684 --- /dev/null +++ b/Winch/Serialization/Shop/DialogueLinkedShopDataConverter.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using static ShopData; +using static ShopData.DialogueLinkedShopData; + +namespace Winch.Serialization.Shop; + +public class DialogueLinkedShopDataConverter : DredgeTypeConverter +{ + private readonly Dictionary _definitions = new() + { + { "itemData", new(new List(), o=>DredgeTypeHelpers.ParseShopItemDataList((JArray)o)) }, + { "dialogueNodes", new( new List(), o=>DredgeTypeHelpers.ParseStringList((JArray)o)) }, + { "requireMode", new( RequireMode.ANY, o => DredgeTypeHelpers.GetEnumValue(o)) } + }; + + public DialogueLinkedShopDataConverter() + { + AddDefinitions(_definitions); + } +} diff --git a/Winch/Serialization/Shop/PhaseLinkedShopDataConverter.cs b/Winch/Serialization/Shop/PhaseLinkedShopDataConverter.cs new file mode 100644 index 00000000..7619d39a --- /dev/null +++ b/Winch/Serialization/Shop/PhaseLinkedShopDataConverter.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using static ShopData; + +namespace Winch.Serialization.Shop; + +public class PhaseLinkedShopDataConverter : DredgeTypeConverter +{ + private readonly Dictionary _definitions = new() + { + { "itemData", new(new List(), o=>DredgeTypeHelpers.ParseShopItemDataList((JArray)o)) }, + { "phase", new(0, o=>int.Parse(o.ToString())) }, + }; + + public PhaseLinkedShopDataConverter() + { + AddDefinitions(_definitions); + } +} diff --git a/Winch/Serialization/Shop/ShopDataConverter.cs b/Winch/Serialization/Shop/ShopDataConverter.cs new file mode 100644 index 00000000..879fb59a --- /dev/null +++ b/Winch/Serialization/Shop/ShopDataConverter.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using Winch.Data.Shop; +using static ShopData; + +namespace Winch.Serialization.Shop; + +public class ShopDataConverter : DredgeTypeConverter +{ + private readonly Dictionary _definitions = new() + { + { "id", new(string.Empty, null) }, + { "alwaysInStock", new(new List(), o=>DredgeTypeHelpers.ParseShopItemDataList((JArray)o)) }, + { "phaseLinkedShopData", new(new List(), o=>DredgeTypeHelpers.ParsePhaseLinkedShopDataList((JArray)o)) }, + { "dialogueLinkedShopData", new(new List(), o=>DredgeTypeHelpers.ParseDialogueLinkedShopDataList((JArray)o)) }, + { "itemSubtypesFromResearchPool", new(new List(), o=>DredgeTypeHelpers.GetEnumValues((JArray)o)) }, + { "countOfEachItemFromResearchPool", new(0, o=>int.Parse(o.ToString())) }, + { "gridKey", new(GridKey.NONE, o=>DredgeTypeHelpers.GetEnumValue(o)) }, + }; + + public ShopDataConverter() + { + AddDefinitions(_definitions); + } +} diff --git a/Winch/Serialization/Shop/ShopItemDataConverter.cs b/Winch/Serialization/Shop/ShopItemDataConverter.cs new file mode 100644 index 00000000..9e272b17 --- /dev/null +++ b/Winch/Serialization/Shop/ShopItemDataConverter.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Winch.Data.Shop; + +namespace Winch.Serialization.Shop; + +public class ShopItemDataConverter : DredgeTypeConverter +{ + private readonly Dictionary _definitions = new() + { + { "itemData", new(string.Empty, null) }, + { "count", new(1, o=>int.Parse(o.ToString())) }, + { "chance", new(1f, o=>float.Parse(o.ToString())) }, + }; + + public ShopItemDataConverter() + { + AddDefinitions(_definitions); + } +} diff --git a/Winch/Util/ShopUtil.cs b/Winch/Util/ShopUtil.cs new file mode 100644 index 00000000..cc4908f3 --- /dev/null +++ b/Winch/Util/ShopUtil.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AeLa.EasyFeedback.APIs; +using Winch.Core; +using Winch.Data.Shop; +using Winch.Serialization.Shop; +using static ShopData; +using static ShopRestocker; +using static Winch.Data.Shop.ModdedShopData; + +namespace Winch.Util; + +public static class ShopUtil +{ + private static ShopDataConverter ShopDataConverter = new ShopDataConverter(); + private static ShopItemDataConverter ShopItemDataConverter = new ShopItemDataConverter(); + private static PhaseLinkedShopDataConverter PhaseLinkedShopDataConverter = new PhaseLinkedShopDataConverter(); + private static DialogueLinkedShopDataConverter DialogueLinkedShopDataConverter = new DialogueLinkedShopDataConverter(); + + internal static bool PopulateShopDataFromMetaWithConverter(ModdedShopData data, Dictionary meta) + { + return UtilHelpers.PopulateObjectFromMeta(data, meta, ShopDataConverter); + } + + internal static bool PopulateShopItemDataFromMetaWithConverter(ModdedShopData.ModdedShopItemData data, Dictionary meta) + { + return UtilHelpers.PopulateObjectFromMeta(data, meta, ShopItemDataConverter); + } + + internal static bool PopulatePhaseLinkedShopDataFromMetaWithConverter(PhaseLinkedShopData data, Dictionary meta) + { + return UtilHelpers.PopulateObjectFromMeta(data, meta, PhaseLinkedShopDataConverter); + } + + internal static bool PopulateDialogueLinkedShopDataFromMetaWithConverter(DialogueLinkedShopData data, Dictionary meta) + { + return UtilHelpers.PopulateObjectFromMeta(data, meta, DialogueLinkedShopDataConverter); + } + + internal static List KeepInStockItems = new(); + internal static Dictionary AllShopDataGridConfigDict = new(); + internal static Dictionary ModdedShopDataDict = new(); + internal static Dictionary AllShopDataDict = new(); + + public static void RegisterKeepInStockItem(string itemID) => KeepInStockItems.Add(itemID); + + public static ModdedShopData GetModdedShopData(string id) + { + if (string.IsNullOrWhiteSpace(id)) + return null; + + if (ModdedShopDataDict.TryGetValue(id, out ModdedShopData shopData)) + return shopData; + else + return null; + } + + public static ShopData GetShopData(string id) + { + if (string.IsNullOrWhiteSpace(id)) + return null; + + if (AllShopDataDict.TryGetValue(id, out var shop)) + return shop; + + if (ModdedShopDataDict.TryGetValue(id, out var moddedShop)) + return moddedShop; + + return null; + } + + internal static void AddModdedShopData(ShopRestocker restocker) + { + restocker.itemIdsToKeep.AddRange(KeepInStockItems); + foreach (var shopData in ModdedShopDataDict.Values) + { + shopData.Populate(); + restocker.shopDataGridConfigs.Add(shopData); + } + } + + internal static void PopulateShopData(IEnumerable result) + { + foreach (var shopDataGridConfig in result) + { + AllShopDataGridConfigDict.SafeAdd(shopDataGridConfig.gridKey, shopDataGridConfig); + WinchCore.Log.Debug($"Added shop data {shopDataGridConfig.gridKey} to AllShopDataGridConfigDict"); + AllShopDataDict.SafeAdd(shopDataGridConfig.shopData.name, shopDataGridConfig.shopData); + WinchCore.Log.Debug($"Added shop data {shopDataGridConfig.shopData.name} to AllShopDataDict"); + } + } + + internal static void ClearShopData() + { + AllShopDataGridConfigDict.Clear(); + AllShopDataDict.Clear(); + } + + internal static void AddShopDataFromMeta(string metaPath) + { + var meta = UtilHelpers.ParseMeta(metaPath); + if (meta == null) + { + WinchCore.Log.Error($"Meta file {metaPath} is empty"); + return; + } + var shopData = UtilHelpers.GetScriptableObjectFromMeta(meta, metaPath); + if (shopData == null) + { + WinchCore.Log.Error($"Couldn't create ModdedShopData"); + return; + } + var id = (string)meta["id"]; + if (ModdedShopDataDict.ContainsKey(id)) + { + WinchCore.Log.Error($"Duplicate shop data {id} at {metaPath} failed to load"); + return; + } + if (PopulateShopDataFromMetaWithConverter(shopData, meta)) + { + ModdedShopDataDict.Add(id, shopData); + } + else + { + WinchCore.Log.Error($"No shop data converter found"); + } + } + + public static ShopData[] GetAllShopData() + { + return AllShopDataDict.Values.ToArray(); + } +}