From 807acf1947c4920cabd934abf39d21a2bcac6b69 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sat, 19 Apr 2025 13:01:10 +0200 Subject: [PATCH] Even more moves --- PkmnLib.Dynamic/Models/BattleChoiceQueue.cs | 33 +++++- .../Models/BattleFlow/MoveTurnExecutor.cs | 2 + PkmnLib.Dynamic/ScriptHandling/Script.cs | 4 + PkmnLib.Tests/Data/Moves.jsonc | 73 ++++++++++-- PkmnLib.Tests/Dynamic/ChoiceQueueTests.cs | 107 ++++++++++++++++++ PkmnLib.Tests/PkmnLib.Tests.csproj | 13 ++- .../Scripts/Moves/Pursuit.cs | 21 ++++ .../Scripts/Moves/Quash.cs | 18 +++ .../Scripts/Moves/QuickGuard.cs | 13 +++ .../PkmnLib.Plugin.Gen7/Scripts/Moves/Rage.cs | 13 +++ .../Scripts/Moves/RagePowder.cs | 18 +++ .../Scripts/Moves/RainDance.cs | 16 +++ .../Scripts/Moves/RapidSpin.cs | 23 ++++ .../Scripts/Moves/RazorWind.cs | 24 ++++ .../Scripts/Pokemon/ChargeMoveEffect.cs | 27 +++++ .../Scripts/Pokemon/PursuitEffect.cs | 52 +++++++++ .../Scripts/Pokemon/RageEffect.cs | 19 ++++ .../Scripts/Side/QuickGuardEffect.cs | 18 +++ .../Scripts/Side/RagePowderEffect.cs | 30 +++++ 19 files changed, 505 insertions(+), 19 deletions(-) create mode 100644 PkmnLib.Tests/Dynamic/ChoiceQueueTests.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Pursuit.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Quash.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/QuickGuard.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Rage.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RagePowder.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RainDance.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RapidSpin.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RazorWind.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeMoveEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PursuitEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/RageEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/QuickGuardEffect.cs create mode 100644 Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/RagePowderEffect.cs diff --git a/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs b/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs index 5813643..7ea9b9f 100644 --- a/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs +++ b/PkmnLib.Dynamic/Models/BattleChoiceQueue.cs @@ -99,8 +99,39 @@ public class BattleChoiceQueue : IDeepCloneable return true; } + /// + /// This moves the choice of a specific Pokémon to the end of the queue, making it the last choice to be executed. + /// + /// + /// Returns true if the Pokémon was found and moved, false otherwise. + /// + public bool MovePokemonChoiceLast(IPokemon pokemon) + { + var index = Array.FindIndex(_choices, _currentIndex, choice => choice?.User == pokemon); + if (index == -1) + return false; + var choice = _choices[index]; + _choices[index] = null; + // Put all choices after the index of the choice forward + for (var i = index; i < _choices.Length - 1; i++) + _choices[i] = _choices[i + 1]; + // And insert the choice at the end + _choices[^1] = choice; + return true; + } + internal IReadOnlyList GetChoices() => _choices; public ITurnChoice? FirstOrDefault(Func predicate) => - _choices.WhereNotNull().FirstOrDefault(predicate); + _choices.Skip(_currentIndex).WhereNotNull().FirstOrDefault(predicate); + + public void Remove(ITurnChoice choice) + { + var index = Array.FindIndex(_choices, _currentIndex, x => x == choice); + if (index == -1) + return; + _choices[index] = null; + for (var i = index; i > _currentIndex; i--) + _choices[i] = _choices[i - 1]; + } } \ No newline at end of file diff --git a/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs b/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs index a41c91a..1cea3ca 100644 --- a/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs +++ b/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs @@ -45,6 +45,8 @@ internal static class MoveTurnExecutor var targets = TargetResolver.ResolveTargets(battle, moveChoice.TargetSide, moveChoice.TargetPosition, targetType); moveChoice.RunScriptHook(x => x.ChangeTargets(moveChoice, ref targets)); + var targetSide = battle.Sides[moveChoice.TargetSide]; + targetSide.RunScriptHook(x => x.ChangeIncomingTargets(moveChoice, ref targets)); byte numberOfHits = 1; moveChoice.RunScriptHook(x => x.ChangeNumberOfHits(moveChoice, ref numberOfHits)); diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs index 700b095..95816a1 100644 --- a/PkmnLib.Dynamic/ScriptHandling/Script.cs +++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs @@ -140,6 +140,10 @@ public abstract class Script : IDeepCloneable { } + public virtual void ChangeIncomingTargets(IMoveChoice moveChoice, ref IReadOnlyList targets) + { + } + /// /// This function allows you to change a move into a multi-hit move. The number of hits set here /// gets used as the number of hits. If set to 0, this will behave as if the move missed on its diff --git a/PkmnLib.Tests/Data/Moves.jsonc b/PkmnLib.Tests/Data/Moves.jsonc index 4e9347c..7a8d07b 100755 --- a/PkmnLib.Tests/Data/Moves.jsonc +++ b/PkmnLib.Tests/Data/Moves.jsonc @@ -8533,7 +8533,10 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "pursuit" + } }, { "name": "quash", @@ -8547,7 +8550,10 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "quash" + } }, { "name": "quick_attack", @@ -8563,6 +8569,7 @@ "protect", "mirror" ] + // No secondary effect }, { "name": "quick_guard", @@ -8575,7 +8582,10 @@ "category": "status", "flags": [ "snatch" - ] + ], + "effect": { + "name": "quick_guard" + } }, { "name": "quiver_dance", @@ -8589,7 +8599,15 @@ "flags": [ "snatch", "dance" - ] + ], + "effect": { + "name": "change_multiple_user_stat_boosts", + "parameters": { + "specialAttack": 1, + "specialDefense": 1, + "speed": 1 + } + } }, { "name": "rage", @@ -8604,7 +8622,10 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "rage" + } }, { "name": "rage_powder", @@ -8617,7 +8638,10 @@ "category": "status", "flags": [ "powder" - ] + ], + "effect": { + "name": "rage_powder" + } }, { "name": "rain_dance", @@ -8628,7 +8652,10 @@ "priority": 0, "target": "All", "category": "status", - "flags": [] + "flags": [], + "effect": { + "name": "rain_dance" + } }, { "name": "rapid_spin", @@ -8643,7 +8670,10 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "rapid_spin" + } }, { "name": "razor_leaf", @@ -8657,7 +8687,10 @@ "flags": [ "protect", "mirror" - ] + ], + "effect": { + "name": "increased_critical_stage" + } }, { "name": "razor_shell", @@ -8672,7 +8705,14 @@ "contact", "protect", "mirror" - ] + ], + "effect": { + "name": "change_target_defense", + "chance": 50, + "parameters": { + "amount": -1 + } + } }, { "name": "razor_wind", @@ -8687,7 +8727,10 @@ "charge", "protect", "mirror" - ] + ], + "effect": { + "name": "razor_wind" + } }, { "name": "recover", @@ -8701,7 +8744,13 @@ "flags": [ "snatch", "heal" - ] + ], + "effect": { + "name": "heal_percent", + "parameters": { + "healPercent": 0.5 + } + } }, { "name": "recycle", diff --git a/PkmnLib.Tests/Dynamic/ChoiceQueueTests.cs b/PkmnLib.Tests/Dynamic/ChoiceQueueTests.cs new file mode 100644 index 0000000..79bab6f --- /dev/null +++ b/PkmnLib.Tests/Dynamic/ChoiceQueueTests.cs @@ -0,0 +1,107 @@ +using Moq; +using PkmnLib.Dynamic.Models; +using PkmnLib.Dynamic.Models.Choices; + +namespace PkmnLib.Tests.Dynamic; + +public class ChoiceQueueTests +{ + [Test] + public async Task ChoiceQueue_MovePokemonChoiceNext() + { + var pokemon1 = new Mock(); + var pokemon2 = new Mock(); + var pokemon3 = new Mock(); + var pokemon4 = new Mock(); + + var choice1 = new Mock(); + choice1.Setup(c => c.User).Returns(pokemon1.Object); + var choice2 = new Mock(); + choice2.Setup(c => c.User).Returns(pokemon2.Object); + var choice3 = new Mock(); + choice3.Setup(c => c.User).Returns(pokemon3.Object); + var choice4 = new Mock(); + choice4.Setup(c => c.User).Returns(pokemon4.Object); + + var queue = new BattleChoiceQueue([choice1.Object, choice2.Object, choice3.Object, choice4.Object]); + var result = queue.MovePokemonChoiceNext(pokemon3.Object); + await Assert.That(result).IsTrue(); + await Assert.That(queue.Dequeue()).IsEqualTo(choice3.Object); + } + + [Test] + public async Task ChoiceQueue_MovePokemonChoiceNextFailsIfAlreadyExecuted() + { + var pokemon1 = new Mock(); + var pokemon2 = new Mock(); + var pokemon3 = new Mock(); + var pokemon4 = new Mock(); + + var choice1 = new Mock(); + choice1.Setup(c => c.User).Returns(pokemon1.Object); + var choice2 = new Mock(); + choice2.Setup(c => c.User).Returns(pokemon2.Object); + var choice3 = new Mock(); + choice3.Setup(c => c.User).Returns(pokemon3.Object); + var choice4 = new Mock(); + choice4.Setup(c => c.User).Returns(pokemon4.Object); + + var queue = new BattleChoiceQueue([choice1.Object, choice2.Object, choice3.Object, choice4.Object]); + queue.Dequeue(); + var result = queue.MovePokemonChoiceNext(pokemon1.Object); + await Assert.That(result).IsFalse(); + await Assert.That(queue.Dequeue()).IsEqualTo(choice2.Object); + } + + [Test] + public async Task ChoiceQueue_MovePokemonChoiceLast() + { + var pokemon1 = new Mock(); + var pokemon2 = new Mock(); + var pokemon3 = new Mock(); + var pokemon4 = new Mock(); + + var choice1 = new Mock(); + choice1.Setup(c => c.User).Returns(pokemon1.Object); + var choice2 = new Mock(); + choice2.Setup(c => c.User).Returns(pokemon2.Object); + var choice3 = new Mock(); + choice3.Setup(c => c.User).Returns(pokemon3.Object); + var choice4 = new Mock(); + choice4.Setup(c => c.User).Returns(pokemon4.Object); + + var queue = new BattleChoiceQueue([choice1.Object, choice2.Object, choice3.Object, choice4.Object]); + var result = queue.MovePokemonChoiceLast(pokemon2.Object); + await Assert.That(result).IsTrue(); + await Assert.That(queue.Dequeue()).IsEqualTo(choice1.Object); + await Assert.That(queue.Dequeue()).IsEqualTo(choice3.Object); + await Assert.That(queue.Dequeue()).IsEqualTo(choice4.Object); + await Assert.That(queue.Dequeue()).IsEqualTo(choice2.Object); + } + + [Test] + public async Task ChoiceQueue_MovePokemonChoiceLastFailsIfAlreadyExecuted() + { + var pokemon1 = new Mock(); + var pokemon2 = new Mock(); + var pokemon3 = new Mock(); + var pokemon4 = new Mock(); + + var choice1 = new Mock(); + choice1.Setup(c => c.User).Returns(pokemon1.Object); + var choice2 = new Mock(); + choice2.Setup(c => c.User).Returns(pokemon2.Object); + var choice3 = new Mock(); + choice3.Setup(c => c.User).Returns(pokemon3.Object); + var choice4 = new Mock(); + choice4.Setup(c => c.User).Returns(pokemon4.Object); + + var queue = new BattleChoiceQueue([choice1.Object, choice2.Object, choice3.Object, choice4.Object]); + queue.Dequeue(); + var result = queue.MovePokemonChoiceLast(pokemon1.Object); + await Assert.That(result).IsFalse(); + await Assert.That(queue.Dequeue()).IsEqualTo(choice2.Object); + await Assert.That(queue.Dequeue()).IsEqualTo(choice3.Object); + await Assert.That(queue.Dequeue()).IsEqualTo(choice4.Object); + } +} \ No newline at end of file diff --git a/PkmnLib.Tests/PkmnLib.Tests.csproj b/PkmnLib.Tests/PkmnLib.Tests.csproj index 77e7a21..c9ed51a 100644 --- a/PkmnLib.Tests/PkmnLib.Tests.csproj +++ b/PkmnLib.Tests/PkmnLib.Tests.csproj @@ -10,18 +10,19 @@ - + + - - + + - + @@ -42,7 +43,7 @@ - - + + diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Pursuit.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Pursuit.cs new file mode 100644 index 0000000..6f0cf1d --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Pursuit.cs @@ -0,0 +1,21 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "pursuit")] +public class Pursuit : Script +{ + /// + public override void OnBeforeTurnStart(ITurnChoice choice) + { + if (choice is IMoveChoice moveChoice) + choice.User.Volatile.Add(new PursuitEffect(moveChoice)); + } + + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) => + move.User.Volatile.Remove(); + + /// + public override void OnAfterMove(IExecutingMove move) => move.User.Volatile.Remove(); +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Quash.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Quash.cs new file mode 100644 index 0000000..92d581e --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Quash.cs @@ -0,0 +1,18 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "quash")] +public class Quash : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + var battleData = move.User.BattleData; + if (battleData == null) + return; + + if (battleData.Battle.ChoiceQueue?.MovePokemonChoiceLast(target) == false) + { + move.GetHitData(target, hit).Fail(); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/QuickGuard.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/QuickGuard.cs new file mode 100644 index 0000000..5e7ae31 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/QuickGuard.cs @@ -0,0 +1,13 @@ +using PkmnLib.Plugin.Gen7.Scripts.Side; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "quick_guard")] +public class QuickGuard : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + move.User.BattleData?.BattleSide.VolatileScripts.Add(new QuickGuardEffect()); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Rage.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Rage.cs new file mode 100644 index 0000000..87c070a --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Rage.cs @@ -0,0 +1,13 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "rage")] +public class Rage : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + move.User.Volatile.Add(new RageEffect()); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RagePowder.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RagePowder.cs new file mode 100644 index 0000000..6dc1ae4 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RagePowder.cs @@ -0,0 +1,18 @@ +using PkmnLib.Plugin.Gen7.Scripts.Side; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "rage_powder")] +public class RagePowder : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + var battleData = move.User.BattleData; + if (battleData == null) + return; + + var effect = battleData.BattleSide.VolatileScripts.Add(new RagePowderEffect(move.User)); + ((RagePowderEffect)effect.Script!).User = move.User; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RainDance.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RainDance.cs new file mode 100644 index 0000000..3ee2fa5 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RainDance.cs @@ -0,0 +1,16 @@ +using PkmnLib.Plugin.Gen7.Scripts.Weather; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "rain_dance")] +public class RainDance : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + var battleData = move.User.BattleData; + if (battleData == null) + return; + battleData.Battle.SetWeather(ScriptUtils.ResolveName(), 5); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RapidSpin.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RapidSpin.cs new file mode 100644 index 0000000..d3502eb --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RapidSpin.cs @@ -0,0 +1,23 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "rapid_spin")] +public class RapidSpin : Script +{ + /// + public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit) + { + move.User.Volatile.Remove(); + move.User.Volatile.Remove(); + move.User.Volatile.Remove(); + move.User.Volatile.Remove(); + // TODO: Sand Tomb effect removal + // TODO: Whirlpool effect removal + // TODO: Wrap effect removal + + // TODO: Remove Spikes + // TODO: Remove Toxic Spikes + // TODO: Remove Stealth Rock + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RazorWind.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RazorWind.cs new file mode 100644 index 0000000..cd55db5 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/RazorWind.cs @@ -0,0 +1,24 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Moves; + +[Script(ScriptCategory.Move, "razor_wind")] +public class RazorWind : Script +{ + /// + public override void PreventMove(IExecutingMove move, ref bool prevent) + { + var chargeMoveEffect = move.User.Volatile.Get(); + if (chargeMoveEffect != null && chargeMoveEffect.MoveName == move.UseMove.Name) + return; + prevent = true; + move.User.Volatile.Add(new ChargeMoveEffect(move.UseMove.Name, move.User, move.MoveChoice.TargetSide, + move.MoveChoice.TargetPosition)); + } + + /// + public override void ChangeCriticalStage(IExecutingMove move, IPokemon target, byte hit, ref byte stage) + { + stage += 1; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeMoveEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeMoveEffect.cs new file mode 100644 index 0000000..89a3d1c --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ChargeMoveEffect.cs @@ -0,0 +1,27 @@ +using PkmnLib.Plugin.Gen7.Scripts.Utils; +using PkmnLib.Static.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "charge_move_effect")] +public class ChargeMoveEffect : Script +{ + public readonly StringKey MoveName; + private readonly IPokemon _user; + private readonly byte _targetSide; + private readonly byte _targetPosition; + + public ChargeMoveEffect(StringKey moveName, IPokemon user, byte targetSide, byte targetPosition) + { + MoveName = moveName; + _user = user; + _targetSide = targetSide; + _targetPosition = targetPosition; + } + + /// + public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice) + { + choice = TurnChoiceHelper.CreateMoveChoice(_user, MoveName, _targetSide, _targetPosition); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PursuitEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PursuitEffect.cs new file mode 100644 index 0000000..bcb6699 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PursuitEffect.cs @@ -0,0 +1,52 @@ +using PkmnLib.Dynamic.Models.BattleFlow; +using PkmnLib.Static.Utils; + +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "pursuit")] +public class PursuitEffect : Script +{ + private readonly IMoveChoice _choice; + + public PursuitEffect(IMoveChoice choice) + { + _choice = choice; + } + + /// + public override void OnSwitchOut(IPokemon oldPokemon, byte position) + { + var battleData = oldPokemon.BattleData; + if (battleData == null) + return; + if (battleData.Battle.HasEnded) + return; + + if (battleData.Position != _choice.TargetPosition || battleData.SideIndex != _choice.TargetSide) + return; + if (!_choice.User.IsUsable) + return; + if (_choice.User.BattleData?.IsOnBattlefield != true) + return; + + var choiceQueue = battleData.Battle.ChoiceQueue; + + var choice = choiceQueue?.FirstOrDefault(x => x == _choice); + if (choice == null) + return; + choiceQueue!.Remove(choice); + _choice.Volatile.Add(new PursuitDoublePowerEffect()); + RemoveSelf(); + TurnRunner.ExecuteChoice(battleData.Battle, _choice); + } + + [Script(ScriptCategory.Pokemon, "pursuit_double_power")] + private class PursuitDoublePowerEffect : Script + { + /// + public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref byte basePower) + { + basePower = basePower.MultiplyOrMax(2); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/RageEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/RageEffect.cs new file mode 100644 index 0000000..a57f565 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/RageEffect.cs @@ -0,0 +1,19 @@ +using PkmnLib.Static; + +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "rage")] +public class RageEffect : Script +{ + /// + public override void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit) + { + move.User.ChangeStatBoost(Statistic.Attack, 1, true); + } + + /// + public override void OnEndTurn(IBattle battle) + { + RemoveSelf(); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/QuickGuardEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/QuickGuardEffect.cs new file mode 100644 index 0000000..42e2892 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/QuickGuardEffect.cs @@ -0,0 +1,18 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Side; + +public class QuickGuardEffect : Script +{ + /// + /// + public override void IsInvulnerableToMove(IExecutingMove move, IPokemon target, ref bool invulnerable) + { + if (move.UseMove.Priority > 0) + invulnerable = true; + } + + /// + public override void OnEndTurn(IBattle battle) + { + RemoveSelf(); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/RagePowderEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/RagePowderEffect.cs new file mode 100644 index 0000000..dcd7238 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/RagePowderEffect.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +namespace PkmnLib.Plugin.Gen7.Scripts.Side; + +[Script(ScriptCategory.Side, "rage_powder")] +public class RagePowderEffect : Script +{ + public IPokemon User { get; set; } + + public RagePowderEffect(IPokemon user) + { + User = user; + } + + /// + public override void ChangeIncomingTargets(IMoveChoice moveChoice, ref IReadOnlyList targets) + { + // Ignore multi-hit moves + if (targets.Count != 1) + return; + + targets = [User]; + } + + /// + public override void OnEndTurn(IBattle battle) + { + RemoveSelf(); + } +} \ No newline at end of file