diff --git a/PkmnLib.Dynamic/Libraries/DataLoaders/Models/SerializedSpecies.cs b/PkmnLib.Dynamic/Libraries/DataLoaders/Models/SerializedSpecies.cs
index 451138e..ff5c935 100644
--- a/PkmnLib.Dynamic/Libraries/DataLoaders/Models/SerializedSpecies.cs
+++ b/PkmnLib.Dynamic/Libraries/DataLoaders/Models/SerializedSpecies.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
@@ -111,11 +112,40 @@ public class SerializedForm
///
public bool IsBattleOnly { get; set; }
+ public string? InheritFrom { get; set; }
+
///
/// Additional data that is not part of the standard form data.
///
[JsonExtensionData]
public Dictionary? ExtensionData { get; set; }
+
+ [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract"),
+ SuppressMessage("ReSharper", "NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract")]
+ internal void MakeInheritFrom(SerializedForm other)
+ {
+ Abilities ??= other.Abilities.ToArray();
+ HiddenAbilities ??= other.HiddenAbilities.ToArray();
+ BaseStats ??= other.BaseStats.Copy();
+ EVReward ??= other.EVReward.Copy();
+ Types ??= other.Types.ToArray();
+ if (Height == 0)
+ Height = other.Height;
+ if (Weight == 0)
+ Weight = other.Weight;
+ if (BaseExp == 0)
+ BaseExp = other.BaseExp;
+ Moves ??= new SerializedMoves();
+ if (Moves.LevelMoves == null || Moves.LevelMoves.Length == 0)
+ Moves.LevelMoves = other.Moves.LevelMoves?.ToArray();
+ if (Moves.EggMoves == null || Moves.EggMoves.Length == 0)
+ Moves.EggMoves = other.Moves.EggMoves?.ToArray();
+ if (Moves.TutorMoves == null || Moves.TutorMoves.Length == 0)
+ Moves.TutorMoves = other.Moves.TutorMoves?.ToArray();
+ if (Moves.Machine == null || Moves.Machine.Length == 0)
+ Moves.Machine = other.Moves.Machine?.ToArray();
+ Flags ??= other.Flags.ToArray();
+ }
}
///
@@ -159,6 +189,17 @@ public record SerializedStats
///
public ushort Speed { get; set; }
+
+ public SerializedStats Copy() =>
+ new()
+ {
+ Hp = Hp,
+ Attack = Attack,
+ Defense = Defense,
+ SpecialAttack = SpecialAttack,
+ SpecialDefense = SpecialDefense,
+ Speed = Speed,
+ };
}
///
diff --git a/PkmnLib.Dynamic/Libraries/DataLoaders/SpeciesDataLoader.cs b/PkmnLib.Dynamic/Libraries/DataLoaders/SpeciesDataLoader.cs
index 70e0acc..e75107f 100644
--- a/PkmnLib.Dynamic/Libraries/DataLoaders/SpeciesDataLoader.cs
+++ b/PkmnLib.Dynamic/Libraries/DataLoaders/SpeciesDataLoader.cs
@@ -76,6 +76,20 @@ public static class SpeciesDataLoader
$"Egg cycles for species {id} is invalid: {serialized.EggCycles}. Must be greater than or equal to 0.");
}
+ foreach (var form in serialized.Formes)
+ {
+ if (!string.IsNullOrEmpty(form.Value.InheritFrom))
+ {
+ var inheritedForm = serialized.Formes.GetValueOrDefault(form.Value.InheritFrom);
+ if (inheritedForm == null)
+ {
+ throw new InvalidDataException(
+ $"Form {form.Key} inherits from {form.Value.InheritFrom}, but that form does not exist.");
+ }
+ form.Value.MakeInheritFrom(inheritedForm);
+ }
+ }
+
var forms = serialized.Formes.ToDictionary(x => (StringKey)x.Key,
x => DeserializeForm(x.Key, x.Value, typeLibrary));
var evolutions = serialized.Evolutions.Select(DeserializeEvolution).ToList();
diff --git a/PkmnLib.Dynamic/Models/Pokemon.cs b/PkmnLib.Dynamic/Models/Pokemon.cs
index ed95315..37b3a9d 100644
--- a/PkmnLib.Dynamic/Models/Pokemon.cs
+++ b/PkmnLib.Dynamic/Models/Pokemon.cs
@@ -784,6 +784,7 @@ public class PokemonImpl : ScriptSource, IPokemon
{
var previous = HeldItem;
HeldItem = item;
+ this.RunScriptHook(x => x.OnAfterHeldItemChange(this, previous, item));
return previous;
}
@@ -799,6 +800,7 @@ public class PokemonImpl : ScriptSource, IPokemon
}
var previous = HeldItem;
HeldItem = null;
+ this.RunScriptHook(x => x.OnAfterHeldItemChange(this, previous, null));
return previous;
}
diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs
index ca026d8..ef403c3 100644
--- a/PkmnLib.Dynamic/ScriptHandling/Script.cs
+++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs
@@ -419,7 +419,7 @@ public abstract class Script : IDeepCloneable
}
///
- /// This function triggers when an opponent on the field faints.
+ /// This function triggers when an opponent on the field faints due to the move that is being executed.
///
public virtual void OnOpponentFaints(IExecutingMove move, IPokemon target, byte hit)
{
@@ -794,4 +794,11 @@ public abstract class Script : IDeepCloneable
ref bool isContact)
{
}
+
+ ///
+ /// This function allows a script to run after a held item has changed.
+ ///
+ public virtual void OnAfterHeldItemChange(IPokemon pokemon, IItem? previous, IItem? item)
+ {
+ }
}
\ No newline at end of file
diff --git a/PkmnLib.Tests/Dataloader/SpeciesDataloaderTests.cs b/PkmnLib.Tests/Dataloader/SpeciesDataloaderTests.cs
index 4e98161..d429a89 100644
--- a/PkmnLib.Tests/Dataloader/SpeciesDataloaderTests.cs
+++ b/PkmnLib.Tests/Dataloader/SpeciesDataloaderTests.cs
@@ -35,4 +35,43 @@ public class SpeciesDataloaderTests
var library = SpeciesDataLoader.LoadSpecies(file, typeLibrary);
await Assert.That(library).IsNotNull();
}
+
+ [Test]
+ public async Task TestPrimarySpeciesFileFormInheritance()
+ {
+ IResourceProvider plugin = new Plugin.Gen7.Gen7Plugin();
+ var result = plugin.GetResource(ResourceFileType.Species)!;
+ await using var file = result.Open();
+ var typeLibrary = new TypeLibrary();
+ typeLibrary.RegisterType("Normal");
+ typeLibrary.RegisterType("Fire");
+ typeLibrary.RegisterType("Water");
+ typeLibrary.RegisterType("Electric");
+ typeLibrary.RegisterType("Grass");
+ typeLibrary.RegisterType("Ice");
+ typeLibrary.RegisterType("Fighting");
+ typeLibrary.RegisterType("Poison");
+ typeLibrary.RegisterType("Ground");
+ typeLibrary.RegisterType("Flying");
+ typeLibrary.RegisterType("Psychic");
+ typeLibrary.RegisterType("Bug");
+ typeLibrary.RegisterType("Rock");
+ typeLibrary.RegisterType("Ghost");
+ typeLibrary.RegisterType("Dragon");
+ typeLibrary.RegisterType("Dark");
+ typeLibrary.RegisterType("Steel");
+ typeLibrary.RegisterType("Fairy");
+
+ var library = SpeciesDataLoader.LoadSpecies(file, typeLibrary);
+ await Assert.That(library).IsNotNull();
+
+ await Assert.That(library.TryGet("arceus", out var species)).IsTrue();
+ await Assert.That(species).IsNotNull();
+ await Assert.That(species!.TryGetForm("arceus_fighting", out var form)).IsTrue();
+ await Assert.That(form).IsNotNull();
+ await Assert.That(form!.Types).HasCount().EqualTo(1);
+ await Assert.That(form.Types[0].Name).IsEqualTo("fighting");
+ await Assert.That(form.Flags).IsEqualTo(species.GetDefaultForm().Flags);
+ await Assert.That(form.BaseStats).IsEqualTo(species.GetDefaultForm().BaseStats);
+ }
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc b/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc
index 3beb10c..c1bc5a1 100755
--- a/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc
+++ b/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc
@@ -369,17 +369,34 @@
"misty_surge": {
"effect": "misty_surge"
},
- "mold_breaker": {},
- "moody": {},
- "motor_drive": {},
- "moxie": {},
- "multiscale": {},
- "multitype": {
- "canBeChanged": false
+ "mold_breaker": {
+ "effect": "mold_breaker"
+ },
+ "moody": {
+ "effect": "moody"
+ },
+ "motor_drive": {
+ "effect": "motor_drive"
+ },
+ "moxie": {
+ "effect": "moxie"
+ },
+ "multiscale": {
+ "effect": "multiscale"
+ },
+ "multitype": {
+ "canBeChanged": false,
+ "effect": "multitype"
+ },
+ "mummy": {
+ "effect": "mummy"
+ },
+ "natural_cure": {
+ "effect": "natural_cure"
+ },
+ "no_guard": {
+ "effect": "no_guard"
},
- "mummy": {},
- "natural_cure": {},
- "no_guard": {},
"normalize": {},
"oblivious": {},
"overcoat": {},
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json b/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json
index 2c52e7a..0f89d5c 100755
--- a/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json
+++ b/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json
@@ -4450,6 +4450,108 @@
],
"formeChange": []
}
+ },
+ "arceus_fighting": {
+ "inheritFrom": "default",
+ "types": [
+ "fighting"
+ ]
+ },
+ "arceus_flying": {
+ "inheritFrom": "default",
+ "types": [
+ "flying"
+ ]
+ },
+ "arceus_bug": {
+ "inheritFrom": "default",
+ "types": [
+ "bug"
+ ]
+ },
+ "arceus_dark": {
+ "inheritFrom": "default",
+ "types": [
+ "dark"
+ ]
+ },
+ "arceus_dragon": {
+ "inheritFrom": "default",
+ "types": [
+ "dragon"
+ ]
+ },
+ "arceus_electric": {
+ "inheritFrom": "default",
+ "types": [
+ "electric"
+ ]
+ },
+ "arceus_fairy": {
+ "inheritFrom": "default",
+ "types": [
+ "fairy"
+ ]
+ },
+ "arceus_fire": {
+ "inheritFrom": "default",
+ "types": [
+ "fire"
+ ]
+ },
+ "arceus_ghost": {
+ "inheritFrom": "default",
+ "types": [
+ "ghost"
+ ]
+ },
+ "arceus_grass": {
+ "inheritFrom": "default",
+ "types": [
+ "grass"
+ ]
+ },
+ "arceus_ground": {
+ "inheritFrom": "default",
+ "types": [
+ "ground"
+ ]
+ },
+ "arceus_ice": {
+ "inheritFrom": "default",
+ "types": [
+ "ice"
+ ]
+ },
+ "arceus_poison": {
+ "inheritFrom": "default",
+ "types": [
+ "poison"
+ ]
+ },
+ "arceus_psychic": {
+ "inheritFrom": "default",
+ "types": [
+ "psychic"
+ ]
+ },
+ "arceus_rock": {
+ "inheritFrom": "default",
+ "types": [
+ "rock"
+ ]
+ },
+ "arceus_steel": {
+ "inheritFrom": "default",
+ "types": [
+ "steel"
+ ]
+ },
+ "arceus_water": {
+ "inheritFrom": "default",
+ "types": [
+ "water"
+ ]
}
},
"evolutions": []
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MoldBreaker.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MoldBreaker.cs
new file mode 100644
index 0000000..b2827f9
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MoldBreaker.cs
@@ -0,0 +1,17 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Mold Breaker is an ability that allows moves to ignore the target's abilities.
+///
+/// Bulbapedia - Mold Breaker
+///
+[Script(ScriptCategory.Ability, "mold_breaker")]
+public class MoldBreaker : Script
+{
+ ///
+ public override void OnBeforeAnyHookInvoked(ref List? suppressedCategories)
+ {
+ suppressedCategories ??= [];
+ suppressedCategories.Add(ScriptCategory.Ability);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Moody.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Moody.cs
new file mode 100644
index 0000000..c22a874
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Moody.cs
@@ -0,0 +1,55 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Moody is an ability that raises one stat and lowers another at the end of each turn.
+///
+/// Bulbapedia - Moody
+///
+[Script(ScriptCategory.Ability, "moody")]
+public class Moody : Script
+{
+ private IPokemon? _pokemon;
+
+ ///
+ public override void OnAddedToParent(IScriptSource source)
+ {
+ if (source is not IPokemon pokemon)
+ throw new InvalidOperationException("Moody script must be attached to a Pokemon.");
+ _pokemon = pokemon;
+ }
+
+ ///
+ public override void OnEndTurn(IBattle battle)
+ {
+ if (_pokemon == null)
+ return;
+
+ var stats = _pokemon.StatBoost;
+ Statistic? raiseStat = null;
+
+ var possibleStatsToRaise = stats.Where(x => x is
+ { value: < 6, statistic: not Statistic.Accuracy and not Statistic.Evasion and not Statistic.Hp })
+ .Select(x => x.statistic).ToList();
+ if (possibleStatsToRaise.Count > 0)
+ {
+ raiseStat = possibleStatsToRaise[battle.Random.GetInt(possibleStatsToRaise.Count)];
+ }
+
+ Statistic? lowerStat = null;
+ var possibleStatsToLower = stats.Where(x => x is
+ {
+ value: > -6,
+ statistic: not Statistic.Accuracy and not Statistic.Evasion and not Statistic.Hp,
+ } && x.statistic != raiseStat).Select(x => x.statistic).ToList();
+
+ if (possibleStatsToLower.Count > 0)
+ {
+ lowerStat = possibleStatsToLower[battle.Random.GetInt(possibleStatsToLower.Count)];
+ }
+
+ if (raiseStat != null)
+ _pokemon.ChangeStatBoost(raiseStat.Value, 1, true, false);
+ if (lowerStat != null)
+ _pokemon.ChangeStatBoost(lowerStat.Value, -1, true, false);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MotorDrive.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MotorDrive.cs
new file mode 100644
index 0000000..d43d333
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/MotorDrive.cs
@@ -0,0 +1,20 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Motor Drive is an ability that grants immunity to Electric-type moves and raises Speed when hit by one.
+///
+/// Bulbapedia - Motor Drive
+///
+[Script(ScriptCategory.Ability, "motor_drive")]
+public class MotorDrive : Script
+{
+ ///
+ public override void IsInvulnerableToMove(IExecutingMove move, IPokemon target, ref bool invulnerable)
+ {
+ if (move.UseMove.MoveType.Name != "electric")
+ return;
+ invulnerable = true;
+ move.Battle.EventHook.Invoke(new AbilityTriggerEvent(target));
+ target.ChangeStatBoost(Statistic.Speed, 1, true, false);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Moxie.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Moxie.cs
new file mode 100644
index 0000000..f5bd48a
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Moxie.cs
@@ -0,0 +1,16 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Moxie is an ability that raises the user's Attack stat after knocking out a Pokémon.
+///
+/// Bulbapedia - Moxie
+///
+[Script(ScriptCategory.Ability, "moxie")]
+public class Moxie : Script
+{
+ ///
+ public override void OnOpponentFaints(IExecutingMove move, IPokemon target, byte hit)
+ {
+ move.User.ChangeStatBoost(Statistic.Attack, 1, true, false);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Multiscale.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Multiscale.cs
new file mode 100644
index 0000000..7258940
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Multiscale.cs
@@ -0,0 +1,19 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Multiscale is an ability that reduces damage taken when at full HP.
+///
+/// Bulbapedia - Multiscale
+///
+[Script(ScriptCategory.Ability, "multiscale")]
+public class Multiscale : Script
+{
+ ///
+ public override void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
+ {
+ if (target.CurrentHealth == target.BoostedStats.Hp)
+ {
+ damage = (uint)(damage * 0.5);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Multitype.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Multitype.cs
new file mode 100644
index 0000000..d3f1776
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Multitype.cs
@@ -0,0 +1,79 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Multitype is an ability that changes the Pokémon's type based on its held Plate or Z-Crystal.
+///
+/// Bulbapedia - Multitype
+///
+[Script(ScriptCategory.Ability, "multitype")]
+public class Multitype : Script
+{
+ ///
+ public override void OnAfterHeldItemChange(IPokemon pokemon, IItem? previous, IItem? item)
+ {
+ if (pokemon.Species.Name != "arceus")
+ return;
+ if (item is null && pokemon.Form.Name != "default")
+ {
+ pokemon.ChangeForm(pokemon.Species.GetDefaultForm());
+ }
+ else if (item is not null && item.Name.ToString().EndsWith("_plate", StringComparison.OrdinalIgnoreCase))
+ {
+ var platePrefix = item.Name.ToString().Replace("_plate", string.Empty, StringComparison.OrdinalIgnoreCase);
+ switch (platePrefix)
+ {
+ case "fist" when pokemon.Species.TryGetForm("arceus_fighting", out var fightingForm):
+ pokemon.ChangeForm(fightingForm);
+ break;
+ case "flame" when pokemon.Species.TryGetForm("arceus_fire", out var fireForm):
+ pokemon.ChangeForm(fireForm);
+ break;
+ case "shock" when pokemon.Species.TryGetForm("arceus_electric", out var electricForm):
+ pokemon.ChangeForm(electricForm);
+ break;
+ case "draco" when pokemon.Species.TryGetForm("arceus_dragon", out var dragonForm):
+ pokemon.ChangeForm(dragonForm);
+ break;
+ case "dread" when pokemon.Species.TryGetForm("arceus_dark", out var darkForm):
+ pokemon.ChangeForm(darkForm);
+ break;
+ case "earth" when pokemon.Species.TryGetForm("arceus_ground", out var groundForm):
+ pokemon.ChangeForm(groundForm);
+ break;
+ case "icicle" when pokemon.Species.TryGetForm("arceus_ice", out var iceForm):
+ pokemon.ChangeForm(iceForm);
+ break;
+ case "insect" when pokemon.Species.TryGetForm("arceus_bug", out var bugForm):
+ pokemon.ChangeForm(bugForm);
+ break;
+ case "iron" when pokemon.Species.TryGetForm("arceus_steel", out var steelForm):
+ pokemon.ChangeForm(steelForm);
+ break;
+ case "meadow" when pokemon.Species.TryGetForm("arceus_grass", out var grassForm):
+ pokemon.ChangeForm(grassForm);
+ break;
+ case "mind" when pokemon.Species.TryGetForm("arceus_psychic", out var psychicForm):
+ pokemon.ChangeForm(psychicForm);
+ break;
+ case "pixie" when pokemon.Species.TryGetForm("arceus_fairy", out var fairyForm):
+ pokemon.ChangeForm(fairyForm);
+ break;
+ case "sky" when pokemon.Species.TryGetForm("arceus_flying", out var flyingForm):
+ pokemon.ChangeForm(flyingForm);
+ break;
+ case "splash" when pokemon.Species.TryGetForm("arceus_water", out var waterForm):
+ pokemon.ChangeForm(waterForm);
+ break;
+ case "spooky" when pokemon.Species.TryGetForm("arceus_ghost", out var ghostForm):
+ pokemon.ChangeForm(ghostForm);
+ break;
+ case "stone" when pokemon.Species.TryGetForm("arceus_rock", out var rockForm):
+ pokemon.ChangeForm(rockForm);
+ break;
+ case "toxic" when pokemon.Species.TryGetForm("arceus_poison", out var poisonForm):
+ pokemon.ChangeForm(poisonForm);
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Mummy.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Mummy.cs
new file mode 100644
index 0000000..83ad8ae
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Mummy.cs
@@ -0,0 +1,21 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Mummy is an ability that changes the attacker's ability to Mummy if it makes contact.
+///
+/// Bulbapedia - Mummy
+///
+[Script(ScriptCategory.Ability, "mummy")]
+public class Mummy : Script
+{
+ ///
+ public override void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit)
+ {
+ if (!move.GetHitData(target, hit).IsContact || move.User.ActiveAbility?.Name == "mummy" ||
+ !move.Battle.Library.StaticLibrary.Abilities.TryGet("mummy", out var mummyAbility))
+ return;
+
+ move.Battle.EventHook.Invoke(new AbilityTriggerEvent(target));
+ move.User.ChangeAbility(mummyAbility);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/NaturalCure.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/NaturalCure.cs
new file mode 100644
index 0000000..29b9665
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/NaturalCure.cs
@@ -0,0 +1,20 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Natural Cure is an ability that heals status conditions when switching out.
+///
+/// Bulbapedia - Natural Cure
+///
+[Script(ScriptCategory.Ability, "natural_cure")]
+public class NaturalCure : Script
+{
+ ///
+ public override void OnSwitchOut(IPokemon oldPokemon, byte position)
+ {
+ if (!oldPokemon.StatusScript.IsEmpty)
+ {
+ oldPokemon.BattleData?.Battle.EventHook.Invoke(new AbilityTriggerEvent(oldPokemon));
+ oldPokemon.ClearStatus();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/NoGuard.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/NoGuard.cs
new file mode 100644
index 0000000..e7b4c71
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/NoGuard.cs
@@ -0,0 +1,24 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// No Guard is an ability that ensures all moves used by and against the Pokémon hit without fail.
+///
+/// Bulbapedia - No Guard
+///
+[Script(ScriptCategory.Ability, "no_guard")]
+public class NoGuard : Script
+{
+ ///
+ public override void ChangeIncomingAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex,
+ ref int modifiedAccuracy)
+ {
+ modifiedAccuracy = 2000;
+ }
+
+ ///
+ public override void ChangeAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex,
+ ref int modifiedAccuracy)
+ {
+ modifiedAccuracy = 2000;
+ }
+}
\ No newline at end of file