diff --git a/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs b/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs
index e669abc..68a0426 100644
--- a/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs
+++ b/PkmnLib.Dynamic/Models/BattleFlow/MoveTurnExecutor.cs
@@ -173,7 +173,9 @@ internal static class MoveTurnExecutor
break;
if (executingMove.GetHitData(target, hitIndex).HasFailed)
break;
- if (useMove.Category == MoveCategory.Status)
+ var category = useMove.Category;
+ executingMove.RunScriptHook(x => x.ChangeCategory(executingMove, target, hitIndex, ref category));
+ if (category == MoveCategory.Status)
{
var secondaryEffect = useMove.SecondaryEffect;
if (secondaryEffect != null)
diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs
index f32ebde..e185a4a 100644
--- a/PkmnLib.Dynamic/ScriptHandling/Script.cs
+++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs
@@ -2,6 +2,7 @@ using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Dynamic.ScriptHandling.Registry;
using PkmnLib.Static;
+using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils;
namespace PkmnLib.Dynamic.ScriptHandling;
@@ -310,14 +311,16 @@ public abstract class Script : IDeepCloneable
///
/// This function allows a script to change the actual offensive stat values used when calculating damage
///
- public virtual void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, ref uint value)
+ public virtual void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
+ ref uint value)
{
}
///
/// This function allows a script to change the actual defensive stat values used when calculating damage.
///
- public virtual void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, ref uint value)
+ public virtual void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
+ ref uint value)
{
}
@@ -603,4 +606,8 @@ public abstract class Script : IDeepCloneable
IList types)
{
}
+
+ public virtual void ChangeCategory(IExecutingMove move, IPokemon target, byte hitIndex, ref MoveCategory category)
+ {
+ }
}
\ No newline at end of file
diff --git a/PkmnLib.Tests/Data/Moves.json b/PkmnLib.Tests/Data/Moves.jsonc
similarity index 98%
rename from PkmnLib.Tests/Data/Moves.json
rename to PkmnLib.Tests/Data/Moves.jsonc
index 8420e70..acccfa2 100755
--- a/PkmnLib.Tests/Data/Moves.json
+++ b/PkmnLib.Tests/Data/Moves.jsonc
@@ -7684,7 +7684,10 @@
"mirror",
"sound",
"ignore-substitute"
- ]
+ ],
+ "effect": {
+ "name": "parting_shot"
+ }
},
{
"name": "pay_day",
@@ -7698,7 +7701,10 @@
"flags": [
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "pay_day"
+ }
},
{
"name": "payback",
@@ -7713,7 +7719,10 @@
"contact",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "payback"
+ }
},
{
"name": "peck",
@@ -7730,6 +7739,7 @@
"mirror",
"distance"
]
+ // No secondary effect
},
{
"name": "perish_song",
@@ -7744,7 +7754,10 @@
"sound",
"distance",
"ignore-substitute"
- ]
+ ],
+ "effect": {
+ "name": "perish_song"
+ }
},
{
"name": "petal_blizzard",
@@ -7759,6 +7772,7 @@
"protect",
"mirror"
]
+ // No secondary effect
},
{
"name": "petal_dance",
@@ -7774,7 +7788,10 @@
"protect",
"mirror",
"dance"
- ]
+ ],
+ "effect": {
+ "name": "petal_dance"
+ }
},
{
"name": "phantom_force",
@@ -7789,7 +7806,10 @@
"contact",
"charge",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "phantom_force"
+ }
},
{
"name": "pin_missile",
@@ -7803,7 +7823,10 @@
"flags": [
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "2_5_hit_move"
+ }
},
{
"name": "play_nice",
@@ -7818,7 +7841,13 @@
"reflectable",
"mirror",
"ignore-substitute"
- ]
+ ],
+ "effect": {
+ "name": "change_target_attack",
+ "parameters": {
+ "amount": -1
+ }
+ }
},
{
"name": "play_rough",
@@ -7833,7 +7862,14 @@
"contact",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "change_target_attack",
+ "chance": 10,
+ "parameters": {
+ "amount": -1
+ }
+ }
},
{
"name": "pluck",
@@ -7849,7 +7885,10 @@
"protect",
"mirror",
"distance"
- ]
+ ],
+ "effect": {
+ "name": "bug_bite"
+ }
},
{
"name": "poison_fang",
@@ -7865,7 +7904,13 @@
"protect",
"mirror",
"bite"
- ]
+ ],
+ "effect": {
+ "name": "set_status",
+ "parameters": {
+ "status": "badly_poisoned"
+ }
+ }
},
{
"name": "poison_gas",
@@ -7880,7 +7925,13 @@
"protect",
"reflectable",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "set_status",
+ "parameters": {
+ "status": "poisoned"
+ }
+ }
},
{
"name": "poison_jab",
@@ -7895,7 +7946,14 @@
"contact",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "set_status",
+ "chance": 30,
+ "parameters": {
+ "status": "poisoned"
+ }
+ }
},
{
"name": "poison_powder",
@@ -7911,7 +7969,13 @@
"reflectable",
"mirror",
"powder"
- ]
+ ],
+ "effect": {
+ "name": "set_status",
+ "parameters": {
+ "status": "poisoned"
+ }
+ }
},
{
"name": "poison_sting",
@@ -7925,7 +7989,14 @@
"flags": [
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "set_status",
+ "chance": 30,
+ "parameters": {
+ "status": "poisoned"
+ }
+ }
},
{
"name": "poison_tail",
@@ -7940,7 +8011,10 @@
"contact",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "poison_tail"
+ }
},
{
"name": "pollen_puff",
@@ -7954,7 +8028,10 @@
"flags": [
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "pollen_puff"
+ }
},
{
"name": "pound",
@@ -7970,6 +8047,7 @@
"protect",
"mirror"
]
+ // No secondary effect
},
{
"name": "powder",
@@ -7986,7 +8064,10 @@
"mirror",
"ignore-substitute",
"powder"
- ]
+ ],
+ "effect": {
+ "name": "powder"
+ }
},
{
"name": "powder_snow",
@@ -8000,7 +8081,14 @@
"flags": [
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "set_status",
+ "chance": 10,
+ "parameters": {
+ "status": "frozen"
+ }
+ }
},
{
"name": "power_gem",
@@ -8015,6 +8103,7 @@
"protect",
"mirror"
]
+ // No secondary effect
},
{
"name": "power_split",
@@ -8027,7 +8116,10 @@
"category": "status",
"flags": [
"protect"
- ]
+ ],
+ "effect": {
+ "name": "power_split"
+ }
},
{
"name": "power_swap",
@@ -8042,7 +8134,10 @@
"protect",
"mirror",
"ignore-substitute"
- ]
+ ],
+ "effect": {
+ "name": "power_swap"
+ }
},
{
"name": "power_trick",
@@ -8055,7 +8150,10 @@
"category": "status",
"flags": [
"snatch"
- ]
+ ],
+ "effect": {
+ "name": "power_trick"
+ }
},
{
"name": "power_trip",
@@ -8070,7 +8168,10 @@
"contact",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "power_trip"
+ }
},
{
"name": "power_up_punch",
@@ -8086,7 +8187,13 @@
"protect",
"mirror",
"punch"
- ]
+ ],
+ "effect": {
+ "name": "change_user_attack",
+ "parameters": {
+ "amount": 1
+ }
+ }
},
{
"name": "power_whip",
@@ -8102,6 +8209,7 @@
"protect",
"mirror"
]
+ // No secondary effect
},
{
"name": "precipice_blades",
@@ -8117,6 +8225,7 @@
"mirror",
"nonskybattle"
]
+ // No secondary effect
},
{
"name": "present",
@@ -10909,7 +11018,10 @@
"contact",
"protect",
"mirror"
- ]
+ ],
+ "effect": {
+ "name": "thrash"
+ }
},
{
"name": "throat_chop",
diff --git a/PkmnLib.Tests/Dataloader/MoveDataLoaderTests.cs b/PkmnLib.Tests/Dataloader/MoveDataLoaderTests.cs
index 3247efa..a7c4aaa 100644
--- a/PkmnLib.Tests/Dataloader/MoveDataLoaderTests.cs
+++ b/PkmnLib.Tests/Dataloader/MoveDataLoaderTests.cs
@@ -8,7 +8,7 @@ public class MoveDataLoaderTests
[Test]
public async Task TestPrimaryMoveFile()
{
- using var stream = File.OpenRead("Data/Moves.json");
+ await using var stream = File.OpenRead("Data/Moves.jsonc");
var typeLibrary = new TypeLibrary();
typeLibrary.RegisterType("Normal");
typeLibrary.RegisterType("Fire");
diff --git a/PkmnLib.Tests/Integration/LibraryHelpers.cs b/PkmnLib.Tests/Integration/LibraryHelpers.cs
index f1c456d..eeb56d1 100644
--- a/PkmnLib.Tests/Integration/LibraryHelpers.cs
+++ b/PkmnLib.Tests/Integration/LibraryHelpers.cs
@@ -13,7 +13,7 @@ public static class LibraryHelpers
var types = TypeDataLoader.LoadTypeLibrary(typesFile);
using var naturesFile = File.Open("Data/Natures.csv", FileMode.Open, FileAccess.Read, FileShare.Read);
var natures = NatureDataLoader.LoadNatureLibrary(naturesFile);
- using var movesFile = File.Open("Data/Moves.json", FileMode.Open, FileAccess.Read, FileShare.Read);
+ using var movesFile = File.Open("Data/Moves.jsonc", FileMode.Open, FileAccess.Read, FileShare.Read);
var moves = MoveDataLoader.LoadMoves(movesFile, types);
using var itemsFile = File.Open("Data/Items.json", FileMode.Open, FileAccess.Read, FileShare.Read);
var items = ItemDataLoader.LoadItems(itemsFile);
@@ -24,14 +24,14 @@ public static class LibraryHelpers
using var speciesFile = File.Open("Data/Pokemon.json", FileMode.Open, FileAccess.Read, FileShare.Read);
var species = SpeciesDataLoader.LoadSpecies(speciesFile, types);
- var staticLibrary = new StaticLibraryImpl(new LibrarySettings()
+ var staticLibrary = new StaticLibraryImpl(new LibrarySettings
{
MaxLevel = 100,
ShinyRate = 4096,
}, species, moves, abilities, types, natures, growthRates, items);
var dynamicLibrary = DynamicLibraryImpl.Create(staticLibrary, [
- new Gen7Plugin(new Gen7PluginConfiguration()
+ new Gen7Plugin(new Gen7PluginConfiguration
{
DamageCalculatorHasRandomness = false,
}),
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7DamageCalculator.cs b/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7DamageCalculator.cs
index 5699958..846337c 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7DamageCalculator.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7DamageCalculator.cs
@@ -133,11 +133,12 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
if (bypassDefense)
targetStats = target.FlatStats;
var defensiveStat = targetStats.GetStatistic(defensive);
+ var origOffensiveStat = offensiveStat;
executingMove.RunScriptHook(script =>
- script.ChangeOffensiveStatValue(executingMove, target, hitNumber, ref offensiveStat));
+ script.ChangeOffensiveStatValue(executingMove, target, hitNumber, defensiveStat, ref offensiveStat));
executingMove.RunScriptHook(script =>
- script.ChangeDefensiveStatValue(executingMove, target, hitNumber, ref defensiveStat));
+ script.ChangeDefensiveStatValue(executingMove, target, hitNumber, origOffensiveStat, ref defensiveStat));
var modifier = (float)offensiveStat / defensiveStat;
executingMove.RunScriptHook(script =>
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FoulPlay.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FoulPlay.cs
index 2e92a0e..60e4c6c 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FoulPlay.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FoulPlay.cs
@@ -6,7 +6,8 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
public class FoulPlay : Script
{
///
- public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, ref uint value)
+ public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint _,
+ ref uint value)
{
value = move.UseMove.Category == MoveCategory.Physical
? target.BoostedStats.Attack
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GuardSplit.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GuardSplit.cs
index c5f652b..308e854 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GuardSplit.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/GuardSplit.cs
@@ -22,6 +22,9 @@ public class GuardSplit : Script
userStats.SetStatistic(Statistic.Defense, newDefense);
userStats.SetStatistic(Statistic.SpecialDefense, newSpecialDefense);
+ targetStats.SetStatistic(Statistic.Defense, newDefense);
+ targetStats.SetStatistic(Statistic.SpecialDefense, newSpecialDefense);
user.RecalculateFlatStats();
+ target.RecalculateFlatStats();
}
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PartingShot.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PartingShot.cs
new file mode 100644
index 0000000..2f4aed4
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PartingShot.cs
@@ -0,0 +1,28 @@
+using PkmnLib.Static;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+///
+/// Implements the secondary effect of Parting Shot, which lowers the target's Attack and Special Attack by one stage each,
+/// then forces the user to switch out if at least one stat change was successful.
+///
+[Script(ScriptCategory.Move, "parting_shot")]
+public class PartingShot : Script
+{
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ var battleData = move.User.BattleData;
+ if (battleData == null)
+ return;
+
+ var evtBatch = new EventBatchId();
+ var attackChanged = target.ChangeStatBoost(Statistic.Attack, -1, false, evtBatch);
+ var specialAttackChanged = target.ChangeStatBoost(Statistic.SpecialAttack, -1, false, evtBatch);
+
+ if (attackChanged || specialAttackChanged)
+ {
+ battleData.BattleSide.SwapPokemon(battleData.Position, null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PayDay.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PayDay.cs
new file mode 100644
index 0000000..bf17af3
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PayDay.cs
@@ -0,0 +1,7 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "pay_day")]
+public class PayDay : Script
+{
+ // TODO: Implement the Pay Day move effect
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Payback.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Payback.cs
new file mode 100644
index 0000000..c2ce94b
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Payback.cs
@@ -0,0 +1,23 @@
+using PkmnLib.Static;
+using PkmnLib.Static.Utils;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+///
+/// Implements the secondary effect of Payback, which doubles the move's power if the user moves after the target.
+///
+[Script(ScriptCategory.Move, "payback")]
+public class Payback : Script
+{
+ ///
+ public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref byte basePower)
+ {
+ var battleData = move.User.BattleData;
+
+ // Check if the target has already moved this turn
+ if (battleData?.Battle.ChoiceQueue?.FirstOrDefault(x => x.User == target) != null)
+ {
+ basePower = basePower.MultiplyOrMax(2);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PerishSong.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PerishSong.cs
new file mode 100644
index 0000000..963dbf0
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PerishSong.cs
@@ -0,0 +1,30 @@
+using System.Linq;
+using PkmnLib.Static;
+using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+using PkmnLib.Static.Utils;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+///
+/// Implements the secondary effect of Perish Song, which causes all Pokemon on the field to faint in 3 turns.
+///
+[Script(ScriptCategory.Move, "perish_song")]
+public class PerishSong : Script
+{
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ if (move.User.Volatile.Contains())
+ return;
+
+ var battleData = move.User.BattleData;
+ if (battleData == null)
+ return;
+
+ // Add Perish Song volatile to all Pokémon on the field
+ foreach (var pokemon in battleData.Battle.Sides.SelectMany(x => x.Pokemon).WhereNotNull())
+ {
+ pokemon.Volatile.Add(new PerishSongEffect(pokemon));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PetalDance.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PetalDance.cs
new file mode 100644
index 0000000..40661b8
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PetalDance.cs
@@ -0,0 +1,26 @@
+using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+///
+/// Implements the secondary effect of Petal Dance, which forces the user to continue using the move for 2-3 turns,
+/// then confuses the user.
+///
+[Script(ScriptCategory.Move, "petal_dance")]
+public class PetalDance : Script
+{
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ if (move.User.Volatile.Contains())
+ return;
+
+ var battleData = move.User.BattleData;
+ if (battleData == null)
+ return;
+
+ var turns = battleData.Battle.Random.GetBool() ? 2 : 3;
+ move.User.Volatile.Add(new PetalDanceEffect(move.User, turns, move.MoveChoice.TargetSide,
+ move.MoveChoice.TargetPosition));
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PhantomForce.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PhantomForce.cs
new file mode 100644
index 0000000..9a8df5e
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PhantomForce.cs
@@ -0,0 +1,36 @@
+using System.Collections.Generic;
+using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+///
+/// Implements the secondary effect of Phantom Force, which makes the user semi-invulnerable on the first turn
+/// and attacks on the second turn.
+///
+[Script(ScriptCategory.Move, "phantom_force")]
+public class PhantomForce : Script
+{
+ ///
+ public override void PreventMove(IExecutingMove move, ref bool prevent)
+ {
+ if (move.User.Volatile.Contains())
+ return;
+
+ move.User.Volatile.Add(new PhantomForceCharge(move.User));
+ move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("phantom_force_charge",
+ new Dictionary
+ {
+ { "user", move.User },
+ }));
+ prevent = true;
+ }
+
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ if (move.User.Volatile.Contains())
+ return;
+
+ move.User.Volatile.Add(new PhantomForceCharge(move.User));
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PoisonTail.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PoisonTail.cs
new file mode 100644
index 0000000..6dd0666
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PoisonTail.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+using PkmnLib.Plugin.Gen7.Scripts.Status;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "poison_tail")]
+public class PoisonTail : Script
+{
+ ///
+ public override void ChangeCriticalStage(IExecutingMove move, IPokemon target, byte hit, ref byte stage)
+ {
+ if (stage == 255)
+ return;
+ stage += 1;
+ }
+
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ var battleData = move.User.BattleData;
+ if (battleData == null)
+ return;
+
+ if (battleData.Battle.Random.EffectChance(10, move, target, hit))
+ {
+ target.SetStatus(ScriptUtils.ResolveName());
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PollenPuff.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PollenPuff.cs
new file mode 100644
index 0000000..f9985fd
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PollenPuff.cs
@@ -0,0 +1,39 @@
+using PkmnLib.Static.Moves;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "pollen_puff")]
+public class PollenPuff : Script
+{
+ ///
+ ///
+ public override void ChangeCategory(IExecutingMove move, IPokemon target, byte hitIndex, ref MoveCategory category)
+ {
+ var battleData = move.User.BattleData;
+ var targetBattleData = target.BattleData;
+
+ if (battleData == null || targetBattleData == null)
+ return;
+
+ if (battleData.SideIndex == targetBattleData.SideIndex)
+ {
+ category = MoveCategory.Status;
+ }
+ }
+
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ var battleData = move.User.BattleData;
+ var targetBattleData = target.BattleData;
+
+ if (battleData == null || targetBattleData == null)
+ return;
+
+ if (battleData.SideIndex == targetBattleData.SideIndex)
+ {
+ var maxHealth = target.MaxHealth;
+ target.Heal(maxHealth / 2);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Powder.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Powder.cs
new file mode 100644
index 0000000..8b34de1
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Powder.cs
@@ -0,0 +1,13 @@
+using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "powder")]
+public class Powder : Script
+{
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ target.Volatile.Add(new PowderEffect());
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerSplit.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerSplit.cs
new file mode 100644
index 0000000..822e201
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerSplit.cs
@@ -0,0 +1,30 @@
+using PkmnLib.Static;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "power_split")]
+public class PowerSplit : Script
+{
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ var user = move.User;
+ var userStats = user.FlatStats;
+ var targetStats = target.FlatStats;
+
+ var userAttack = userStats.GetStatistic(Statistic.Attack);
+ var targetAttack = targetStats.GetStatistic(Statistic.Attack);
+ var userSpecialAttack = userStats.GetStatistic(Statistic.SpecialAttack);
+ var targetSpecialAttack = targetStats.GetStatistic(Statistic.SpecialAttack);
+
+ var newAttack = (userAttack + targetAttack) / 2;
+ var newSpecialAttack = (userSpecialAttack + targetSpecialAttack) / 2;
+
+ userStats.SetStatistic(Statistic.Attack, newAttack);
+ userStats.SetStatistic(Statistic.SpecialAttack, newSpecialAttack);
+ targetStats.SetStatistic(Statistic.Attack, newAttack);
+ targetStats.SetStatistic(Statistic.SpecialAttack, newSpecialAttack);
+ user.RecalculateFlatStats();
+ target.RecalculateFlatStats();
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerSwap.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerSwap.cs
new file mode 100644
index 0000000..12a232f
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerSwap.cs
@@ -0,0 +1,26 @@
+using PkmnLib.Static;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "power_swap")]
+public class PowerSwap : Script
+{
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ var user = move.User;
+
+ var eventBatchId = new EventBatchId();
+ var userAttack = user.StatBoost.Attack;
+ var targetAttack = target.StatBoost.Attack;
+ var userSpecialAttack = user.StatBoost.SpecialAttack;
+ var targetSpecialAttack = target.StatBoost.SpecialAttack;
+
+ user.ChangeStatBoost(Statistic.Attack, (sbyte)(targetAttack - userAttack), true, eventBatchId);
+ user.ChangeStatBoost(Statistic.SpecialAttack, (sbyte)(targetSpecialAttack - userSpecialAttack), true,
+ eventBatchId);
+ target.ChangeStatBoost(Statistic.Attack, (sbyte)(userAttack - targetAttack), true, eventBatchId);
+ target.ChangeStatBoost(Statistic.SpecialAttack, (sbyte)(userSpecialAttack - targetSpecialAttack), true,
+ eventBatchId);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerTrick.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerTrick.cs
new file mode 100644
index 0000000..93904ea
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerTrick.cs
@@ -0,0 +1,13 @@
+using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "power_trick")]
+public class PowerTrick : Script
+{
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ move.User.Volatile.Add(new PowerTrickEffect());
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerTrip.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerTrip.cs
new file mode 100644
index 0000000..e1d23fe
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/PowerTrip.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Linq;
+using PkmnLib.Static;
+using PkmnLib.Static.Utils;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+[Script(ScriptCategory.Move, "power_trip")]
+public class PowerTrip : Script
+{
+ ///
+ public override void ChangeMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
+ {
+ var modifier = 1;
+ foreach (Statistic stat in Enum.GetValues(typeof(Statistic)))
+ {
+ if (stat is Statistic.Accuracy or Statistic.Evasion)
+ continue;
+ var statChange = move.User.StatBoost.GetStatistic(stat);
+ if (statChange > 0)
+ modifier += statChange;
+ }
+
+ damage = damage.MultiplyOrMax(modifier);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Thrash.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Thrash.cs
new file mode 100644
index 0000000..629bcb8
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Thrash.cs
@@ -0,0 +1,26 @@
+using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
+
+///
+/// Implements the secondary effect of Thrash, which forces the user to continue using the move for 2-3 turns,
+/// then confuses the user.
+///
+[Script(ScriptCategory.Move, "thrash")]
+public class Thrash : Script
+{
+ ///
+ public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
+ {
+ if (move.User.Volatile.Contains())
+ return;
+
+ var battleData = move.User.BattleData;
+ if (battleData == null)
+ return;
+
+ var turns = battleData.Battle.Random.GetBool() ? 2 : 3;
+ move.User.Volatile.Add(new ThrashEffect(move.User, turns, move.MoveChoice.TargetSide,
+ move.MoveChoice.TargetPosition));
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/OutrageEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/OutrageEffect.cs
index 7c31a44..a39d8d0 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/OutrageEffect.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/OutrageEffect.cs
@@ -1,37 +1,10 @@
-using PkmnLib.Plugin.Gen7.Scripts.Utils;
-
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "outrage")]
-public class OutrageEffect : Script
+public class OutrageEffect : OutrageLikeEffect
{
- private readonly IPokemon _owner;
- private int _turns;
- private readonly byte _targetSide;
- private readonly byte _targetPosition;
-
- public OutrageEffect(IPokemon owner, int turns, byte targetSide, byte targetPosition)
+ public OutrageEffect(IPokemon owner, int turns, byte targetSide, byte targetPosition) : base(owner, turns,
+ targetSide, targetPosition, "outrage")
{
- _owner = owner;
- _turns = turns;
- _targetSide = targetSide;
- _targetPosition = targetPosition;
- }
-
- ///
- public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice)
- {
- choice = TurnChoiceHelper.CreateMoveChoice(_owner, "outrage", _targetSide, _targetPosition);
- }
-
- ///
- public override void OnAfterHits(IExecutingMove move, IPokemon target)
- {
- _turns--;
- if (_turns <= 0)
- {
- RemoveSelf();
- _owner.Volatile.Add(new Confusion());
- }
}
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/OutrageLikeEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/OutrageLikeEffect.cs
new file mode 100644
index 0000000..409253e
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/OutrageLikeEffect.cs
@@ -0,0 +1,39 @@
+using PkmnLib.Plugin.Gen7.Scripts.Utils;
+using PkmnLib.Static.Utils;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+public abstract class OutrageLikeEffect : Script
+{
+ private readonly IPokemon _owner;
+ private int _turns;
+ private readonly byte _targetSide;
+ private readonly byte _targetPosition;
+ private readonly StringKey _move;
+
+ public OutrageLikeEffect(IPokemon owner, int turns, byte targetSide, byte targetPosition, StringKey move)
+ {
+ _owner = owner;
+ _turns = turns;
+ _targetSide = targetSide;
+ _targetPosition = targetPosition;
+ _move = move;
+ }
+
+ ///
+ public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice)
+ {
+ choice = TurnChoiceHelper.CreateMoveChoice(_owner, "_move", _targetSide, _targetPosition);
+ }
+
+ ///
+ public override void OnAfterHits(IExecutingMove move, IPokemon target)
+ {
+ _turns--;
+ if (_turns <= 0)
+ {
+ RemoveSelf();
+ _owner.Volatile.Add(new Confusion());
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PerishSongEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PerishSongEffect.cs
new file mode 100644
index 0000000..7dc1766
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PerishSongEffect.cs
@@ -0,0 +1,27 @@
+using PkmnLib.Static;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+[Script(ScriptCategory.Pokemon, "perish_song")]
+public class PerishSongEffect : Script
+{
+ private int _turns;
+ private IPokemon _owner;
+
+ public PerishSongEffect(IPokemon owner, int turns = 3)
+ {
+ _owner = owner;
+ _turns = turns;
+ }
+
+ ///
+ public override void OnEndTurn(IBattle battle)
+ {
+ _turns--;
+ if (_turns <= 0)
+ {
+ RemoveSelf();
+ _owner.Faint(DamageSource.Misc);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PetalDanceEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PetalDanceEffect.cs
new file mode 100644
index 0000000..4ceb9d4
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PetalDanceEffect.cs
@@ -0,0 +1,10 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+[Script(ScriptCategory.Pokemon, "petal_dance")]
+public class PetalDanceEffect : OutrageLikeEffect
+{
+ public PetalDanceEffect(IPokemon owner, int turns, byte targetSide, byte targetPosition) : base(owner, turns,
+ targetSide, targetPosition, "petal_dance")
+ {
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PhantomForceCharge.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PhantomForceCharge.cs
new file mode 100644
index 0000000..55d8892
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PhantomForceCharge.cs
@@ -0,0 +1,27 @@
+using PkmnLib.Plugin.Gen7.Scripts.Utils;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+[Script(ScriptCategory.Pokemon, "phantom_force")]
+public class PhantomForceCharge : Script
+{
+ private readonly IPokemon _owner;
+
+ public PhantomForceCharge(IPokemon owner)
+ {
+ _owner = owner;
+ }
+
+ ///
+ public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice)
+ {
+ var opposingSideIndex = (byte)(_owner.BattleData?.SideIndex == 0 ? 1 : 0);
+ choice = TurnChoiceHelper.CreateMoveChoice(_owner, "phantom_force", opposingSideIndex, position);
+ }
+
+ ///
+ public override void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
+ {
+ block = true;
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PowderEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PowderEffect.cs
new file mode 100644
index 0000000..13faff8
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PowderEffect.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+[Script(ScriptCategory.Pokemon, "powder")]
+public class PowderEffect : Script
+{
+ ///
+ ///
+ public override void BlockOutgoingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
+ {
+ var hit = executingMove.GetHitData(target, hitIndex);
+ if (hit.Type.Name == "fire")
+ {
+ executingMove.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("powder_explodes",
+ new Dictionary
+ {
+ { "user", executingMove.User },
+ { "target", target },
+ }));
+
+ var health = executingMove.User.MaxHealth / 4;
+ executingMove.User.Damage(health, DamageSource.Misc);
+
+ block = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PowerTrickEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PowerTrickEffect.cs
new file mode 100644
index 0000000..da0e747
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/PowerTrickEffect.cs
@@ -0,0 +1,19 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+[Script(ScriptCategory.Pokemon, "power_trick")]
+public class PowerTrickEffect : Script
+{
+ ///
+ public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
+ ref uint value)
+ {
+ value = defensiveStat;
+ }
+
+ ///
+ public override void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
+ ref uint value)
+ {
+ value = offensiveStat;
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ThrashEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ThrashEffect.cs
new file mode 100644
index 0000000..5a51ea1
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/ThrashEffect.cs
@@ -0,0 +1,10 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
+
+[Script(ScriptCategory.Pokemon, "thrash")]
+public class ThrashEffect : OutrageLikeEffect
+{
+ public ThrashEffect(IPokemon owner, int turns, byte targetSide, byte targetPosition) : base(owner, turns,
+ targetSide, targetPosition, "thrash")
+ {
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/BadlyPoisoned.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/BadlyPoisoned.cs
new file mode 100644
index 0000000..c05ebea
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/BadlyPoisoned.cs
@@ -0,0 +1,6 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Status;
+
+[Script(ScriptCategory.Status, "badly_poisoned")]
+public class BadlyPoisoned : Script
+{
+}
\ No newline at end of file