diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index f5cde9e..29fd77f 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -10,7 +10,7 @@
"rollForward": false
},
"jetbrains.resharper.globaltools": {
- "version": "2024.3.6",
+ "version": "2025.1.1",
"commands": [
"jb"
],
diff --git a/Directory.Build.props b/Directory.Build.props
index 0ebcca0..e802dc8 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,6 +4,7 @@
+ nullable
true
diff --git a/PkmnLib.Dynamic/Models/Battle.cs b/PkmnLib.Dynamic/Models/Battle.cs
index bfc83a6..c792161 100644
--- a/PkmnLib.Dynamic/Models/Battle.cs
+++ b/PkmnLib.Dynamic/Models/Battle.cs
@@ -172,6 +172,7 @@ public class BattleImpl : ScriptSource, IBattle
CanFlee = canFlee;
NumberOfSides = numberOfSides;
PositionsPerSide = positionsPerSide;
+ Volatile = new ScriptSet(this);
var sides = new IBattleSide[numberOfSides];
for (byte i = 0; i < numberOfSides; i++)
sides[i] = new BattleSideImpl(i, positionsPerSide, this);
@@ -399,7 +400,7 @@ public class BattleImpl : ScriptSource, IBattle
}
///
- public IScriptSet Volatile { get; } = new ScriptSet();
+ public IScriptSet Volatile { get; }
///
public StringKey? WeatherName => WeatherScript.Script?.Name;
diff --git a/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs b/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs
index bdbf276..1cc8c48 100644
--- a/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs
+++ b/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs
@@ -132,6 +132,9 @@ public class BattleChoiceQueue : IDeepCloneable
public ITurnChoice? FirstOrDefault(Func predicate) =>
_choices.Skip(_currentIndex).WhereNotNull().FirstOrDefault(predicate);
+ public IEnumerable Where(Func predicate) =>
+ _choices.Skip(_currentIndex).WhereNotNull().Where(predicate);
+
///
/// Removes a choice from the queue.
///
diff --git a/PkmnLib.Dynamic/Models/BattleSide.cs b/PkmnLib.Dynamic/Models/BattleSide.cs
index d975487..3d9673f 100644
--- a/PkmnLib.Dynamic/Models/BattleSide.cs
+++ b/PkmnLib.Dynamic/Models/BattleSide.cs
@@ -174,7 +174,7 @@ public class BattleSideImpl : ScriptSource, IBattleSide
_fillablePositions[i] = true;
}
Battle = battle;
- VolatileScripts = new ScriptSet();
+ VolatileScripts = new ScriptSet(this);
}
///
diff --git a/PkmnLib.Dynamic/Models/Choices/MoveChoice.cs b/PkmnLib.Dynamic/Models/Choices/MoveChoice.cs
index 206931d..8d7dd53 100644
--- a/PkmnLib.Dynamic/Models/Choices/MoveChoice.cs
+++ b/PkmnLib.Dynamic/Models/Choices/MoveChoice.cs
@@ -55,6 +55,7 @@ public class MoveChoice : TurnChoice, IMoveChoice
ChosenMove = usedMove;
TargetSide = targetSide;
TargetPosition = targetPosition;
+ Volatile = new ScriptSet(this);
var secondaryEffect = usedMove.MoveData.SecondaryEffect;
if (secondaryEffect != null)
@@ -86,7 +87,7 @@ public class MoveChoice : TurnChoice, IMoveChoice
public Dictionary? AdditionalData { get; }
///
- public IScriptSet Volatile { get; } = new ScriptSet();
+ public IScriptSet Volatile { get; }
///
public override int ScriptCount => 2 + User.ScriptCount;
diff --git a/PkmnLib.Dynamic/Models/LearnedMove.cs b/PkmnLib.Dynamic/Models/LearnedMove.cs
index 49423a9..c84af85 100644
--- a/PkmnLib.Dynamic/Models/LearnedMove.cs
+++ b/PkmnLib.Dynamic/Models/LearnedMove.cs
@@ -37,6 +37,11 @@ public enum MoveLearnMethod
/// The move is learned when the Pokémon changes form.
///
FormChange,
+
+ ///
+ /// The move is learned by using a move sketch.
+ ///
+ Sketch,
}
///
diff --git a/PkmnLib.Dynamic/Models/Pokemon.cs b/PkmnLib.Dynamic/Models/Pokemon.cs
index d851011..cc14e3f 100644
--- a/PkmnLib.Dynamic/Models/Pokemon.cs
+++ b/PkmnLib.Dynamic/Models/Pokemon.cs
@@ -487,6 +487,7 @@ public class PokemonImpl : ScriptSource, IPokemon
WeightInKg = form.Weight;
HeightInMeters = form.Height;
Happiness = species.BaseHappiness;
+ Volatile = new ScriptSet(this);
if (!library.StaticLibrary.Natures.TryGet(natureName, out var nature))
throw new KeyNotFoundException($"Nature {natureName} not found.");
Nature = nature;
@@ -532,6 +533,7 @@ public class PokemonImpl : ScriptSource, IPokemon
AbilityIndex = form.FindAbilityIndex(ability) ??
throw new KeyNotFoundException(
$"Ability {ability.Name} not found on species {species.Name} form {form.Name}.");
+ Volatile = new ScriptSet(this);
_learnedMoves = serializedPokemon.Moves.Select(move =>
{
if (move == null)
@@ -736,7 +738,7 @@ public class PokemonImpl : ScriptSource, IPokemon
public ScriptContainer StatusScript { get; } = new();
///
- public IScriptSet Volatile { get; } = new ScriptSet();
+ public IScriptSet Volatile { get; }
///
public bool HasHeldItem(StringKey itemName) => HeldItem?.Name == itemName;
@@ -1097,6 +1099,11 @@ public class PokemonImpl : ScriptSource, IPokemon
{
if (!Library.ScriptResolver.TryResolve(ScriptCategory.Status, status, null, out var statusScript))
throw new KeyNotFoundException($"Status script {status} not found");
+ var preventStatus = false;
+ this.RunScriptHook(script => script.PreventStatusChange(this, status, ref preventStatus));
+ if (preventStatus)
+ return false;
+
StatusScript.Set(statusScript);
return true;
}
diff --git a/PkmnLib.Dynamic/PkmnLib.Dynamic.csproj b/PkmnLib.Dynamic/PkmnLib.Dynamic.csproj
index beac537..bf8ac7c 100644
--- a/PkmnLib.Dynamic/PkmnLib.Dynamic.csproj
+++ b/PkmnLib.Dynamic/PkmnLib.Dynamic.csproj
@@ -4,7 +4,6 @@
netstandard2.1
12
enable
- nullable
enable
diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs
index 924a9ab..bb8a4c3 100644
--- a/PkmnLib.Dynamic/ScriptHandling/Script.cs
+++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics;
using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Dynamic.ScriptHandling.Registry;
@@ -12,6 +13,7 @@ namespace PkmnLib.Dynamic.ScriptHandling;
/// changes. This allows for easily defining generational differences, and add effects that the
/// developer might require.
///
+[DebuggerDisplay("{Category} - {Name}")]
public abstract class Script : IDeepCloneable
{
internal event Action