using System.Linq.Expressions; using System.Reflection; using JetBrains.Annotations; using PkmnLib.Dynamic.Libraries; using PkmnLib.Static.Utils; namespace PkmnLib.Dynamic.ScriptHandling.Registry; /// <summary> /// A helper data class that's passed through all plugins when initializing a library, so they can /// register their scripts and other data. /// </summary> public class ScriptRegistry { private readonly Dictionary<(ScriptCategory category, StringKey name), Func<Script>> _scriptTypes = new(); private IBattleStatCalculator? _battleStatCalculator; private IDamageCalculator? _damageCalculator; private IMiscLibrary? _miscLibrary; /// <summary> /// Automatically register all scripts in the given assembly that have the <see cref="ScriptAttribute"/>, and /// inherit from <see cref="Script"/>. /// </summary> public void RegisterAssemblyScripts(Assembly assembly) { var baseType = typeof(Script); foreach (var type in assembly.GetTypes().Where(t => baseType.IsAssignableFrom(t))) { var attribute = type.GetCustomAttribute<ScriptAttribute>(); if (attribute == null) continue; RegisterScriptType(attribute.Category, attribute.Name, type); } } /// <summary> /// Register a script type with the given category and name. /// </summary> [PublicAPI] public void RegisterScriptType(ScriptCategory category, StringKey name, Type type) { if (type == null) throw new ArgumentNullException(nameof(type)); var constructor = type.GetConstructor(Type.EmptyTypes); if (constructor == null) throw new ArgumentException("The type must have a parameterless constructor."); // We create a lambda that creates a new instance of the script type. // This is more performant than using Activator.CreateInstance. _scriptTypes[(category, name)] = Expression.Lambda<Func<Script>>(Expression.New(constructor)).Compile(); } /// <summary> /// Register a battle stat calculator. /// </summary> public void RegisterBattleStatCalculator<T>(T battleStatCalculator) where T : IBattleStatCalculator => _battleStatCalculator = battleStatCalculator; /// <summary> /// Register a damage calculator. /// </summary> public void RegisterDamageCalculator<T>(T damageCalculator) where T : IDamageCalculator => _damageCalculator = damageCalculator; /// <summary> /// Register a misc library. /// </summary> public void RegisterMiscLibrary<T>(T miscLibrary) where T : IMiscLibrary => _miscLibrary = miscLibrary; internal Dictionary<(ScriptCategory category, StringKey name), Func<Script>> ScriptTypes => _scriptTypes; internal IBattleStatCalculator? BattleStatCalculator => _battleStatCalculator; internal IDamageCalculator? DamageCalculator => _damageCalculator; internal IMiscLibrary? MiscLibrary => _miscLibrary; }