diff --git a/src/ModBehaviour.cs b/src/ModBehaviour.cs
index 304d144..f333793 100644
--- a/src/ModBehaviour.cs
+++ b/src/ModBehaviour.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using UnityEngine;
using ItemStatsSystem;
using SodaCraft.Localizations;
+using Duckov.Modding;
using Duckov.Utilities;
namespace TradingCardMod
@@ -17,6 +18,11 @@ namespace TradingCardMod
private static ModBehaviour? _instance;
public static ModBehaviour Instance => _instance!;
+ ///
+ /// Mod name for ModConfig integration.
+ ///
+ public const string MOD_NAME = "TradingCardMod";
+
///
/// Gets the list of registered card items (for loot injection).
///
@@ -128,6 +134,114 @@ namespace TradingCardMod
}
}
+ ///
+ /// Called when the mod is enabled. Set up ModConfig integration.
+ ///
+ void OnEnable()
+ {
+ ModManager.OnModActivated += OnModActivated;
+
+ // Check if ModConfig is already loaded
+ if (ModConfigAPI.IsAvailable())
+ {
+ Debug.Log("[TradingCardMod] ModConfig already available");
+ SetupModConfig();
+ }
+ }
+
+ ///
+ /// Called when another mod is activated.
+ ///
+ private void OnModActivated(ModInfo info, Duckov.Modding.ModBehaviour behaviour)
+ {
+ if (info.name == ModConfigAPI.ModConfigName)
+ {
+ Debug.Log("[TradingCardMod] ModConfig activated");
+ SetupModConfig();
+ }
+ }
+
+ ///
+ /// Called when the mod is disabled. Clean up ModConfig subscriptions.
+ ///
+ void OnDisable()
+ {
+ ModManager.OnModActivated -= OnModActivated;
+ }
+
+ ///
+ /// Set up ModConfig to display loaded card sets information.
+ /// Uses dropdowns with single options to create read-only displays.
+ ///
+ private void SetupModConfig()
+ {
+ if (!ModConfigAPI.IsAvailable())
+ {
+ return;
+ }
+
+ // Build info string showing loaded card sets
+ var setInfo = new List();
+ foreach (var setEntry in _cardsBySet)
+ {
+ setInfo.Add($"{setEntry.Key}: {setEntry.Value.Count} cards");
+ }
+
+ string loadedSetsInfo = setInfo.Count > 0
+ ? string.Join(", ", setInfo)
+ : "No card sets loaded";
+
+ // Use dropdowns with single options to create read-only displays
+ // Total cards display
+ var cardsOption = new System.Collections.Generic.SortedDictionary
+ {
+ { $"{_loadedCards.Count} cards", _loadedCards.Count }
+ };
+ ModConfigAPI.SafeAddDropdownList(
+ MOD_NAME,
+ "TotalCards",
+ "Total Cards Loaded",
+ cardsOption,
+ typeof(int),
+ _loadedCards.Count
+ );
+
+ // Total packs display
+ var packsOption = new System.Collections.Generic.SortedDictionary
+ {
+ { $"{_registeredPacks.Count} packs", _registeredPacks.Count }
+ };
+ ModConfigAPI.SafeAddDropdownList(
+ MOD_NAME,
+ "TotalPacks",
+ "Total Packs Loaded",
+ packsOption,
+ typeof(int),
+ _registeredPacks.Count
+ );
+
+ // Card sets info - one entry per set for clarity
+ int setIndex = 0;
+ foreach (var setEntry in _cardsBySet)
+ {
+ var setOption = new System.Collections.Generic.SortedDictionary
+ {
+ { $"{setEntry.Value.Count} cards", setEntry.Value.Count }
+ };
+ ModConfigAPI.SafeAddDropdownList(
+ MOD_NAME,
+ $"Set_{setIndex}",
+ $"Set: {setEntry.Key}",
+ setOption,
+ typeof(int),
+ setEntry.Value.Count
+ );
+ setIndex++;
+ }
+
+ Debug.Log("[TradingCardMod] ModConfig setup completed");
+ }
+
///
/// Scans the CardSets directory and loads all card definitions.
///
diff --git a/src/ModConfigApi.cs b/src/ModConfigApi.cs
new file mode 100644
index 0000000..8e3afa2
--- /dev/null
+++ b/src/ModConfigApi.cs
@@ -0,0 +1,331 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using UnityEngine;
+
+namespace TradingCardMod
+{
+ ///
+ /// ModConfig Safe API Wrapper Class - Provides non-throwing static interfaces.
+ /// Adapted from ModConfigExample by FrozenFish259.
+ ///
+ public static class ModConfigAPI
+ {
+ public static string ModConfigName = "ModConfig";
+
+ // Ensure this matches the number of ModConfig.ModBehaviour.VERSION
+ private const int ModConfigVersion = 1;
+
+ private static string TAG = $"[TradingCardMod] ModConfig_v{ModConfigVersion}";
+
+ private static Type modBehaviourType;
+ private static Type optionsManagerType;
+ public static bool isInitialized = false;
+ private static bool versionChecked = false;
+ private static bool isVersionCompatible = false;
+
+ ///
+ /// Check version compatibility.
+ ///
+ private static bool CheckVersionCompatibility()
+ {
+ if (versionChecked)
+ return isVersionCompatible;
+
+ try
+ {
+ FieldInfo versionField = modBehaviourType.GetField("VERSION", BindingFlags.Public | BindingFlags.Static);
+ if (versionField != null && versionField.FieldType == typeof(int))
+ {
+ int modConfigVersion = (int)versionField.GetValue(null);
+ isVersionCompatible = (modConfigVersion == ModConfigVersion);
+
+ if (!isVersionCompatible)
+ {
+ Debug.LogError($"{TAG} Version mismatch! API version: {ModConfigVersion}, ModConfig version: {modConfigVersion}");
+ return false;
+ }
+
+ Debug.Log($"{TAG} Version check passed: {ModConfigVersion}");
+ versionChecked = true;
+ return true;
+ }
+ else
+ {
+ Debug.LogWarning($"{TAG} Version field not found, skipping version check");
+ isVersionCompatible = true;
+ versionChecked = true;
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"{TAG} Version check failed: {ex.Message}");
+ isVersionCompatible = false;
+ versionChecked = true;
+ return false;
+ }
+ }
+
+ ///
+ /// Initialize ModConfigAPI, check if necessary functions exist.
+ ///
+ public static bool Initialize()
+ {
+ try
+ {
+ if (isInitialized)
+ return true;
+
+ modBehaviourType = FindTypeInAssemblies("ModConfig.ModBehaviour");
+ if (modBehaviourType == null)
+ {
+ Debug.LogWarning($"{TAG} ModConfig.ModBehaviour type not found, ModConfig may not be loaded");
+ return false;
+ }
+
+ optionsManagerType = FindTypeInAssemblies("ModConfig.OptionsManager_Mod");
+ if (optionsManagerType == null)
+ {
+ Debug.LogWarning($"{TAG} ModConfig.OptionsManager_Mod type not found");
+ return false;
+ }
+
+ if (!CheckVersionCompatibility())
+ {
+ Debug.LogWarning($"{TAG} ModConfig version mismatch!");
+ return false;
+ }
+
+ string[] requiredMethods = {
+ "AddDropdownList",
+ "AddInputWithSlider",
+ "AddBoolDropdownList",
+ "AddOnOptionsChangedDelegate",
+ "RemoveOnOptionsChangedDelegate",
+ };
+
+ foreach (string methodName in requiredMethods)
+ {
+ MethodInfo method = modBehaviourType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
+ if (method == null)
+ {
+ Debug.LogError($"{TAG} Required method {methodName} not found");
+ return false;
+ }
+ }
+
+ isInitialized = true;
+ Debug.Log($"{TAG} ModConfigAPI initialized successfully");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"{TAG} Initialization failed: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Find type in all loaded assemblies.
+ ///
+ private static Type FindTypeInAssemblies(string typeName)
+ {
+ try
+ {
+ Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
+
+ foreach (Assembly assembly in assemblies)
+ {
+ try
+ {
+ Type type = assembly.GetType(typeName);
+ if (type != null)
+ {
+ Debug.Log($"{TAG} Found type {typeName} in assembly {assembly.FullName}");
+ return type;
+ }
+ }
+ catch
+ {
+ continue;
+ }
+ }
+
+ return null;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"{TAG} Assembly scan failed: {ex.Message}");
+ return null;
+ }
+ }
+
+ ///
+ /// Safely add options changed event delegate.
+ ///
+ public static bool SafeAddOnOptionsChangedDelegate(Action action)
+ {
+ if (!Initialize())
+ return false;
+
+ if (action == null)
+ return false;
+
+ try
+ {
+ MethodInfo method = modBehaviourType.GetMethod("AddOnOptionsChangedDelegate", BindingFlags.Public | BindingFlags.Static);
+ method.Invoke(null, new object[] { action });
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"{TAG} Failed to add options changed delegate: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Safely remove options changed event delegate.
+ ///
+ public static bool SafeRemoveOnOptionsChangedDelegate(Action action)
+ {
+ if (!Initialize())
+ return false;
+
+ if (action == null)
+ return false;
+
+ try
+ {
+ MethodInfo method = modBehaviourType.GetMethod("RemoveOnOptionsChangedDelegate", BindingFlags.Public | BindingFlags.Static);
+ method.Invoke(null, new object[] { action });
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"{TAG} Failed to remove options changed delegate: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Safely add input with slider configuration item.
+ ///
+ public static bool SafeAddInputWithSlider(string modName, string key, string description, Type valueType, object defaultValue, Vector2? sliderRange = null)
+ {
+ key = $"{modName}_{key}";
+
+ if (!Initialize())
+ return false;
+
+ try
+ {
+ MethodInfo method = modBehaviourType.GetMethod("AddInputWithSlider", BindingFlags.Public | BindingFlags.Static);
+
+ object[] parameters = sliderRange.HasValue ?
+ new object[] { modName, key, description, valueType, defaultValue, sliderRange.Value } :
+ new object[] { modName, key, description, valueType, defaultValue, null };
+
+ method.Invoke(null, parameters);
+
+ Debug.Log($"{TAG} Added input with slider: {modName}.{key}");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"{TAG} Failed to add input with slider {modName}.{key}: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Safely add boolean dropdown list configuration item.
+ ///
+ public static bool SafeAddBoolDropdownList(string modName, string key, string description, bool defaultValue)
+ {
+ key = $"{modName}_{key}";
+
+ if (!Initialize())
+ return false;
+
+ try
+ {
+ MethodInfo method = modBehaviourType.GetMethod("AddBoolDropdownList", BindingFlags.Public | BindingFlags.Static);
+ method.Invoke(null, new object[] { modName, key, description, defaultValue });
+
+ Debug.Log($"{TAG} Added bool dropdown: {modName}.{key}");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"{TAG} Failed to add bool dropdown {modName}.{key}: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Safely add dropdown list configuration item.
+ ///
+ public static bool SafeAddDropdownList(string modName, string key, string description, System.Collections.Generic.SortedDictionary options, Type valueType, object defaultValue)
+ {
+ key = $"{modName}_{key}";
+
+ if (!Initialize())
+ return false;
+
+ try
+ {
+ MethodInfo method = modBehaviourType.GetMethod("AddDropdownList", BindingFlags.Public | BindingFlags.Static);
+ method.Invoke(null, new object[] { modName, key, description, options, valueType, defaultValue });
+
+ Debug.Log($"{TAG} Added dropdown list: {modName}.{key}");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"{TAG} Failed to add dropdown list {modName}.{key}: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Safely load configuration value.
+ ///
+ public static T SafeLoad(string mod_name, string key, T defaultValue = default(T))
+ {
+ key = $"{mod_name}_{key}";
+
+ if (!Initialize())
+ return defaultValue;
+
+ if (string.IsNullOrEmpty(key))
+ return defaultValue;
+
+ try
+ {
+ MethodInfo loadMethod = optionsManagerType.GetMethod("Load", BindingFlags.Public | BindingFlags.Static);
+ if (loadMethod == null)
+ return defaultValue;
+
+ MethodInfo genericLoadMethod = loadMethod.MakeGenericMethod(typeof(T));
+ object result = genericLoadMethod.Invoke(null, new object[] { key, defaultValue });
+
+ return (T)result;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"{TAG} Failed to load config {key}: {ex.Message}");
+ return defaultValue;
+ }
+ }
+
+ ///
+ /// Check if ModConfig is available.
+ ///
+ public static bool IsAvailable()
+ {
+ return Initialize();
+ }
+ }
+}