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