From 7c2845502d4f3be47f63e8902af6d29e0faf8b9c Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Mon, 14 Apr 2025 15:29:26 +0200 Subject: [PATCH] Bunch more moves, changes in how additional information for items works. --- PkmnLib.Dataloader/ItemDataLoader.cs | 11 +- PkmnLib.Dataloader/Models/SerializedItem.cs | 4 +- .../Models/BattleFlow/MoveTurnExecutor.cs | 30 +- PkmnLib.Dynamic/Models/BattleSide.cs | 1 + PkmnLib.Dynamic/Models/ExecutingMove.cs | 7 +- .../ScriptHandling/Registry/ScriptUtils.cs | 30 +- PkmnLib.Dynamic/ScriptHandling/Script.cs | 29 + .../ScriptHandling/ScriptExecution.cs | 20 + PkmnLib.Dynamic/ScriptHandling/ScriptSet.cs | 5 + PkmnLib.Static/Item.cs | 39 +- PkmnLib.Static/Utils/EnumerableHelpers.cs | 9 + PkmnLib.Static/Utils/NumericHelpers.cs | 6 + PkmnLib.Tests/Data/Items.json | 3616 +++++++++++++---- PkmnLib.Tests/Data/Moves.json | 341 +- .../Scripts/Moves/MultiAttackTests.cs | 89 + .../Scripts/Battle/Gravity.cs | 26 +- .../Scripts/Battle/MagicRoomEffect.cs | 32 + .../Scripts/Battle/MudSportEffect.cs | 28 + .../Scripts/MoveVolatile/MeFirstPowerBoost.cs | 11 + .../Moves/ChangeMultipleTargetStatBoosts.cs | 42 + .../Scripts/Moves/Fling.cs | 7 +- .../Scripts/Moves/FlyingPress.cs | 16 +- .../Scripts/Moves/MagicRoom.cs | 18 + .../Scripts/Moves/MagmaStorm.cs | 13 + .../Scripts/Moves/MagnetRise.cs | 19 + .../Scripts/Moves/Magnitude.cs | 52 + .../Scripts/Moves/MatBlock.cs | 22 + .../Scripts/Moves/MeFirst.cs | 31 + .../Scripts/Moves/MeanLook.cs | 15 + .../Scripts/Moves/Memento.cs | 17 + .../Scripts/Moves/MetalBurst.cs | 51 + .../Scripts/Moves/Metronome.cs | 38 + .../Scripts/Moves/Mimic.cs | 9 + .../Scripts/Moves/MiracleEye.cs | 18 + .../Scripts/Moves/MirrorCoat.cs | 51 + .../Scripts/Moves/MirrorMove.cs | 30 + .../PkmnLib.Plugin.Gen7/Scripts/Moves/Mist.cs | 17 + .../Scripts/Moves/MistyTerrain.cs | 14 + .../Scripts/Moves/MoongeistBeam.cs | 14 + .../Scripts/Moves/Moonlight.cs | 28 + .../Scripts/Moves/MudSport.cs | 17 + .../Scripts/Moves/MultiAttack.cs | 20 + .../Scripts/Moves/NaturalGift.cs | 60 + .../Scripts/Moves/NaturePower.cs | 7 + .../Scripts/Moves/NaturesMadness.cs | 12 + .../Scripts/Moves/NightShade.cs | 11 + .../Scripts/Moves/Nightmare.cs | 19 + .../Scripts/Pokemon/ForesightEffect.cs | 14 +- .../Scripts/Pokemon/IngrainEffect.cs | 27 +- .../Scripts/Pokemon/MagmaStormEffect.cs | 24 + .../Scripts/Pokemon/MagnetRiseEffect.cs | 25 + .../Scripts/Pokemon/MeanLookEffect.cs | 29 + .../Scripts/Pokemon/MiracleEyeEffect.cs | 29 + .../Scripts/Pokemon/NightmareEffect.cs | 26 + .../Scripts/Side/MatBlockEffect.cs | 19 + .../Scripts/Side/MistEffect.cs | 15 + .../Scripts/Terrain/MistyTerrain.cs | 7 + .../Scripts/Weather/Rain.cs | 7 + .../Scripts/Weather/Sandstorm.cs | 7 + .../Scripts/Weather/Sunny.cs | 7 + 60 files changed, 4275 insertions(+), 963 deletions(-) create mode 100644 Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/MultiAttackTests.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/MagicRoomEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/MudSportEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/MoveVolatile/MeFirstPowerBoost.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/ChangeMultipleTargetStatBoosts.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MagicRoom.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MagmaStorm.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MagnetRise.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Magnitude.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MatBlock.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MeFirst.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MeanLook.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Memento.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MetalBurst.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Metronome.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Mimic.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MiracleEye.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MirrorCoat.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MirrorMove.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Mist.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MistyTerrain.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MoongeistBeam.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Moonlight.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MudSport.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MultiAttack.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/NaturalGift.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/NaturePower.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/NaturesMadness.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/NightShade.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Nightmare.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/MagmaStormEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/MagnetRiseEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/MeanLookEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/MiracleEyeEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/NightmareEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/MatBlockEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/MistEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Terrain/MistyTerrain.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Rain.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Sandstorm.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Weather/Sunny.cs diff --git a/PkmnLib.Dataloader/ItemDataLoader.cs b/PkmnLib.Dataloader/ItemDataLoader.cs index 63b9f91..9ffa64e 100644 --- a/PkmnLib.Dataloader/ItemDataLoader.cs +++ b/PkmnLib.Dataloader/ItemDataLoader.cs @@ -29,12 +29,12 @@ public static class ItemDataLoader public delegate IItem ItemFactoryDelegate(SerializedItem serialized, StringKey name, ItemCategory type, BattleItemCategory battleType, int price, ImmutableHashSet flags, ISecondaryEffect? effect, - ISecondaryEffect? battleTriggerEffect, byte flingPower); + ISecondaryEffect? battleTriggerEffect, Dictionary additionalData); [PublicAPI] public static ItemFactoryDelegate ItemConstructor { get; set; } = - (_, name, type, battleType, price, flags, effect, battleTriggerEffect, flingPower) => new ItemImpl(name, type, - battleType, price, flags, effect, battleTriggerEffect, flingPower); + (_, name, type, battleType, price, flags, effect, battleTriggerEffect, additionalData) => new ItemImpl(name, + type, battleType, price, flags, effect, battleTriggerEffect, additionalData); private static IItem DeserializeItem(SerializedItem serialized) { @@ -43,9 +43,12 @@ public static class ItemDataLoader Enum.TryParse(serialized.BattleType, true, out BattleItemCategory battleType); var effect = serialized.Effect?.ParseEffect(); var battleTriggerEffect = serialized.BattleEffect?.ParseEffect(); + var additionalData = + serialized.AdditionalData?.ToDictionary(x => (StringKey)x.Key, x => x.Value.ToParameter()) ?? + new Dictionary(); return ItemConstructor(serialized, serialized.Name, itemType, battleType, serialized.Price, serialized.Flags.Select(x => (StringKey)x).ToImmutableHashSet(), effect, battleTriggerEffect, - serialized.FlingPower); + additionalData); } } \ No newline at end of file diff --git a/PkmnLib.Dataloader/Models/SerializedItem.cs b/PkmnLib.Dataloader/Models/SerializedItem.cs index 05baa84..c831690 100644 --- a/PkmnLib.Dataloader/Models/SerializedItem.cs +++ b/PkmnLib.Dataloader/Models/SerializedItem.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.Json.Serialization; namespace PkmnLib.Dataloader.Models; @@ -11,9 +12,10 @@ public class SerializedItem public string BattleType { get; set; } = null!; public string[] Flags { get; set; } = null!; public int Price { get; set; } - public byte FlingPower { get; set; } public SerializedMoveEffect? Effect { get; set; } public SerializedMoveEffect? BattleEffect { get; set; } + public Dictionary? AdditionalData { get; set; } = null!; + [JsonExtensionData] public Dictionary? ExtensionData { get; set; } } \ No newline at end of file diff --git a/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs b/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs index 3023ac5..ddc4543 100644 --- a/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs +++ b/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs @@ -11,18 +11,18 @@ internal static class MoveTurnExecutor internal static void ExecuteMoveChoice(IBattle battle, IMoveChoice moveChoice) { var chosenMove = moveChoice.ChosenMove; - var moveData = chosenMove.MoveData; + var useMove = chosenMove.MoveData; - var moveDataName = moveData.Name; + var moveDataName = useMove.Name; moveChoice.RunScriptHook(x => x.ChangeMove(moveChoice, ref moveDataName)); - if (moveData.Name != moveDataName) + if (useMove.Name != moveDataName) { - if (!battle.Library.StaticLibrary.Moves.TryGet(moveDataName, out moveData)) + if (!battle.Library.StaticLibrary.Moves.TryGet(moveDataName, out useMove)) { throw new InvalidOperationException( $"The move was changed to '{moveDataName}' by a script, but this move does not exist."); } - var secondaryEffect = moveData.SecondaryEffect; + var secondaryEffect = useMove.SecondaryEffect; if (secondaryEffect != null) { if (moveChoice.User.Library.ScriptResolver.TryResolve(ScriptCategory.Move, secondaryEffect.Name, @@ -30,10 +30,18 @@ internal static class MoveTurnExecutor { moveChoice.Script.Set(script); } + else + { + moveChoice.Script.Clear(); + } + } + else + { + moveChoice.Script.Clear(); } } - var targetType = moveData.Target; + var targetType = useMove.Target; var targets = TargetResolver.ResolveTargets(battle, moveChoice.TargetSide, moveChoice.TargetPosition, targetType); moveChoice.RunScriptHook(x => x.ChangeTargets(moveChoice, ref targets)); @@ -45,7 +53,7 @@ internal static class MoveTurnExecutor return; } - var executingMove = new ExecutingMoveImpl(targets, numberOfHits, chosenMove, moveData, moveChoice); + var executingMove = new ExecutingMoveImpl(targets, numberOfHits, chosenMove, useMove, moveChoice); var prevented = false; executingMove.RunScriptHook(x => x.PreventMove(executingMove, ref prevented)); @@ -117,8 +125,14 @@ internal static class MoveTurnExecutor var hitData = (HitData)executingMove.GetDataFromRawIndex(targetHitStat + i); hitData.Type = hitType; - var effectiveness = battle.Library.StaticLibrary.Types.GetEffectiveness(hitType, target.Types); + var types = target.Types.ToList(); + executingMove.RunScriptHook(x => x.ChangeTypesForMove(executingMove, target, hitIndex, types)); + target.RunScriptHook(x => x.ChangeTypesForIncomingMove(executingMove, target, hitIndex, types)); + + var effectiveness = battle.Library.StaticLibrary.Types.GetEffectiveness(hitType, types); executingMove.RunScriptHook(x => x.ChangeEffectiveness(executingMove, target, hitIndex, ref effectiveness)); + target.RunScriptHook(x => + x.ChangeIncomingEffectiveness(executingMove, target, hitIndex, ref effectiveness)); hitData.Effectiveness = effectiveness; var blockCritical = false; diff --git a/PkmnLib.Dynamic/Models/BattleSide.cs b/PkmnLib.Dynamic/Models/BattleSide.cs index d44c0db..c18c9c2 100644 --- a/PkmnLib.Dynamic/Models/BattleSide.cs +++ b/PkmnLib.Dynamic/Models/BattleSide.cs @@ -235,6 +235,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide var oldPokemon = _pokemon[position]; if (oldPokemon is not null) { + oldPokemon.RunScriptHook(script => script.OnSwitchOut(oldPokemon, position)); oldPokemon.RunScriptHook(script => script.OnRemove()); oldPokemon.SetOnBattlefield(false); } diff --git a/PkmnLib.Dynamic/Models/ExecutingMove.cs b/PkmnLib.Dynamic/Models/ExecutingMove.cs index f948483..be41e79 100644 --- a/PkmnLib.Dynamic/Models/ExecutingMove.cs +++ b/PkmnLib.Dynamic/Models/ExecutingMove.cs @@ -183,6 +183,8 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove /// public ScriptContainer Script => MoveChoice.Script; + public IScriptSet Volatile => MoveChoice.Volatile; + /// public IHitData GetHitData(IPokemon target, byte hit) { @@ -224,18 +226,19 @@ public class ExecutingMoveImpl : ScriptSource, IExecutingMove public IMoveChoice MoveChoice { get; } /// - public override int ScriptCount => 1 + User.ScriptCount; + public override int ScriptCount => 2 + User.ScriptCount; /// public override void GetOwnScripts(List> scripts) { + scripts.Add(Volatile); scripts.Add(Script); } /// public override void CollectScripts(List> scripts) { - scripts.Add(Script); + GetOwnScripts(scripts); User.CollectScripts(scripts); } } \ No newline at end of file diff --git a/PkmnLib.Dynamic/ScriptHandling/Registry/ScriptUtils.cs b/PkmnLib.Dynamic/ScriptHandling/Registry/ScriptUtils.cs index 8f7d5a4..6d0bb98 100644 --- a/PkmnLib.Dynamic/ScriptHandling/Registry/ScriptUtils.cs +++ b/PkmnLib.Dynamic/ScriptHandling/Registry/ScriptUtils.cs @@ -8,26 +8,46 @@ namespace PkmnLib.Dynamic.ScriptHandling.Registry; /// public static class ScriptUtils { - private static readonly Dictionary NameCache = new(); + private static readonly Dictionary Cache = new(); /// /// Resolve name from the of the given script. /// public static StringKey ResolveName(this Script script) => ResolveName(script.GetType()); + /// + /// Resolve name from the of the given type. + /// public static StringKey ResolveName() where T : Script => ResolveName(typeof(T)); /// /// Resolve name from the of the given type. /// - public static StringKey ResolveName(Type type) + public static StringKey ResolveName(Type type) => GetFromCacheOrAdd(type).name; + + /// + /// Resolve category from the of the given script. + /// + public static ScriptCategory ResolveCategory(this Script script) => ResolveCategory(script.GetType()); + + /// + /// Resolve category from the of the given script. + /// + public static ScriptCategory ResolveCategory() where T : Script => ResolveCategory(typeof(T)); + + /// + /// Resolve category from the of the given type. + /// + public static ScriptCategory ResolveCategory(Type type) => GetFromCacheOrAdd(type).category; + + private static (ScriptCategory category, StringKey name) GetFromCacheOrAdd(Type type) { - if (NameCache.TryGetValue(type, out var name)) - return name; + if (Cache.TryGetValue(type, out var key)) + return key; var scriptAttr = type.GetCustomAttribute(); if (scriptAttr == null) throw new InvalidOperationException($"Type {type} does not have a {nameof(ScriptAttribute)}."); - return NameCache[type] = scriptAttr.Name; + return Cache[type] = (scriptAttr.Category, scriptAttr.Name); } } \ No newline at end of file diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs index 6ce488c..d8da606 100644 --- a/PkmnLib.Dynamic/ScriptHandling/Script.cs +++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs @@ -29,6 +29,12 @@ public abstract class Script : IDeepCloneable /// public virtual StringKey Name => this.ResolveName(); + /// + /// The category of a script is used to determine what kind of script it is. This is used to + /// determine what kind of script it is, and what kind of events it can handle. + /// + public virtual ScriptCategory Category => this.ResolveCategory(); + /// /// A script can be suppressed by other scripts. If a script is suppressed by at least one script /// we will not execute its methods. This should return the number of suppressions on the script. @@ -55,6 +61,10 @@ public abstract class Script : IDeepCloneable /// public void Unsuppress() => _suppressCount--; + public virtual void OnBeforeAnyHookInvoked(ref List? suppressedCategories) + { + } + /// /// This function is ran when a volatile effect is added while that volatile effect already is /// in place. Instead of adding the volatile effect twice, it will execute this function instead. @@ -206,6 +216,11 @@ public abstract class Script : IDeepCloneable { } + public virtual void ChangeIncomingEffectiveness(IExecutingMove executingMove, IPokemon target, byte hitIndex, + ref float effectiveness) + { + } + /// /// This function allows a script to block an outgoing move from being critical. /// @@ -473,6 +488,10 @@ public abstract class Script : IDeepCloneable { } + public virtual void OnSwitchOut(IPokemon oldPokemon, byte position) + { + } + /// /// This function is triggered on a Pokemon and its parents when the given Pokemon is switched into /// the battlefield. @@ -567,4 +586,14 @@ public abstract class Script : IDeepCloneable public virtual void PreventHeal(IPokemon pokemon, uint heal, bool allowRevive, ref bool prevented) { } + + public virtual void ChangeTypesForMove(IExecutingMove executingMove, IPokemon target, byte hitIndex, + IList types) + { + } + + public virtual void ChangeTypesForIncomingMove(IExecutingMove executingMove, IPokemon target, byte hitIndex, + IList types) + { + } } \ No newline at end of file diff --git a/PkmnLib.Dynamic/ScriptHandling/ScriptExecution.cs b/PkmnLib.Dynamic/ScriptHandling/ScriptExecution.cs index cc33ac5..0ef88a4 100644 --- a/PkmnLib.Dynamic/ScriptHandling/ScriptExecution.cs +++ b/PkmnLib.Dynamic/ScriptHandling/ScriptExecution.cs @@ -16,6 +16,14 @@ public static class ScriptExecution public static void RunScriptHook(this IScriptSource source, Action