More moves

This commit is contained in:
Deukhoofd 2025-04-17 13:07:45 +02:00
parent 1b54c78b07
commit d02c05874b
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
31 changed files with 682 additions and 65 deletions

View File

@ -173,7 +173,9 @@ internal static class MoveTurnExecutor
break; break;
if (executingMove.GetHitData(target, hitIndex).HasFailed) if (executingMove.GetHitData(target, hitIndex).HasFailed)
break; 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; var secondaryEffect = useMove.SecondaryEffect;
if (secondaryEffect != null) if (secondaryEffect != null)

View File

@ -2,6 +2,7 @@ using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.Models.Choices; using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Dynamic.ScriptHandling.Registry; using PkmnLib.Dynamic.ScriptHandling.Registry;
using PkmnLib.Static; using PkmnLib.Static;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils; using PkmnLib.Static.Utils;
namespace PkmnLib.Dynamic.ScriptHandling; namespace PkmnLib.Dynamic.ScriptHandling;
@ -310,14 +311,16 @@ public abstract class Script : IDeepCloneable
/// <summary> /// <summary>
/// This function allows a script to change the actual offensive stat values used when calculating damage /// This function allows a script to change the actual offensive stat values used when calculating damage
/// </summary> /// </summary>
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)
{ {
} }
/// <summary> /// <summary>
/// This function allows a script to change the actual defensive stat values used when calculating damage. /// This function allows a script to change the actual defensive stat values used when calculating damage.
/// </summary> /// </summary>
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<TypeIdentifier> types) IList<TypeIdentifier> types)
{ {
} }
public virtual void ChangeCategory(IExecutingMove move, IPokemon target, byte hitIndex, ref MoveCategory category)
{
}
} }

View File

@ -7684,7 +7684,10 @@
"mirror", "mirror",
"sound", "sound",
"ignore-substitute" "ignore-substitute"
] ],
"effect": {
"name": "parting_shot"
}
}, },
{ {
"name": "pay_day", "name": "pay_day",
@ -7698,7 +7701,10 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "pay_day"
}
}, },
{ {
"name": "payback", "name": "payback",
@ -7713,7 +7719,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "payback"
}
}, },
{ {
"name": "peck", "name": "peck",
@ -7730,6 +7739,7 @@
"mirror", "mirror",
"distance" "distance"
] ]
// No secondary effect
}, },
{ {
"name": "perish_song", "name": "perish_song",
@ -7744,7 +7754,10 @@
"sound", "sound",
"distance", "distance",
"ignore-substitute" "ignore-substitute"
] ],
"effect": {
"name": "perish_song"
}
}, },
{ {
"name": "petal_blizzard", "name": "petal_blizzard",
@ -7759,6 +7772,7 @@
"protect", "protect",
"mirror" "mirror"
] ]
// No secondary effect
}, },
{ {
"name": "petal_dance", "name": "petal_dance",
@ -7774,7 +7788,10 @@
"protect", "protect",
"mirror", "mirror",
"dance" "dance"
] ],
"effect": {
"name": "petal_dance"
}
}, },
{ {
"name": "phantom_force", "name": "phantom_force",
@ -7789,7 +7806,10 @@
"contact", "contact",
"charge", "charge",
"mirror" "mirror"
] ],
"effect": {
"name": "phantom_force"
}
}, },
{ {
"name": "pin_missile", "name": "pin_missile",
@ -7803,7 +7823,10 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "2_5_hit_move"
}
}, },
{ {
"name": "play_nice", "name": "play_nice",
@ -7818,7 +7841,13 @@
"reflectable", "reflectable",
"mirror", "mirror",
"ignore-substitute" "ignore-substitute"
] ],
"effect": {
"name": "change_target_attack",
"parameters": {
"amount": -1
}
}
}, },
{ {
"name": "play_rough", "name": "play_rough",
@ -7833,7 +7862,14 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "change_target_attack",
"chance": 10,
"parameters": {
"amount": -1
}
}
}, },
{ {
"name": "pluck", "name": "pluck",
@ -7849,7 +7885,10 @@
"protect", "protect",
"mirror", "mirror",
"distance" "distance"
] ],
"effect": {
"name": "bug_bite"
}
}, },
{ {
"name": "poison_fang", "name": "poison_fang",
@ -7865,7 +7904,13 @@
"protect", "protect",
"mirror", "mirror",
"bite" "bite"
] ],
"effect": {
"name": "set_status",
"parameters": {
"status": "badly_poisoned"
}
}
}, },
{ {
"name": "poison_gas", "name": "poison_gas",
@ -7880,7 +7925,13 @@
"protect", "protect",
"reflectable", "reflectable",
"mirror" "mirror"
] ],
"effect": {
"name": "set_status",
"parameters": {
"status": "poisoned"
}
}
}, },
{ {
"name": "poison_jab", "name": "poison_jab",
@ -7895,7 +7946,14 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "set_status",
"chance": 30,
"parameters": {
"status": "poisoned"
}
}
}, },
{ {
"name": "poison_powder", "name": "poison_powder",
@ -7911,7 +7969,13 @@
"reflectable", "reflectable",
"mirror", "mirror",
"powder" "powder"
] ],
"effect": {
"name": "set_status",
"parameters": {
"status": "poisoned"
}
}
}, },
{ {
"name": "poison_sting", "name": "poison_sting",
@ -7925,7 +7989,14 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "set_status",
"chance": 30,
"parameters": {
"status": "poisoned"
}
}
}, },
{ {
"name": "poison_tail", "name": "poison_tail",
@ -7940,7 +8011,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "poison_tail"
}
}, },
{ {
"name": "pollen_puff", "name": "pollen_puff",
@ -7954,7 +8028,10 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "pollen_puff"
}
}, },
{ {
"name": "pound", "name": "pound",
@ -7970,6 +8047,7 @@
"protect", "protect",
"mirror" "mirror"
] ]
// No secondary effect
}, },
{ {
"name": "powder", "name": "powder",
@ -7986,7 +8064,10 @@
"mirror", "mirror",
"ignore-substitute", "ignore-substitute",
"powder" "powder"
] ],
"effect": {
"name": "powder"
}
}, },
{ {
"name": "powder_snow", "name": "powder_snow",
@ -8000,7 +8081,14 @@
"flags": [ "flags": [
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "set_status",
"chance": 10,
"parameters": {
"status": "frozen"
}
}
}, },
{ {
"name": "power_gem", "name": "power_gem",
@ -8015,6 +8103,7 @@
"protect", "protect",
"mirror" "mirror"
] ]
// No secondary effect
}, },
{ {
"name": "power_split", "name": "power_split",
@ -8027,7 +8116,10 @@
"category": "status", "category": "status",
"flags": [ "flags": [
"protect" "protect"
] ],
"effect": {
"name": "power_split"
}
}, },
{ {
"name": "power_swap", "name": "power_swap",
@ -8042,7 +8134,10 @@
"protect", "protect",
"mirror", "mirror",
"ignore-substitute" "ignore-substitute"
] ],
"effect": {
"name": "power_swap"
}
}, },
{ {
"name": "power_trick", "name": "power_trick",
@ -8055,7 +8150,10 @@
"category": "status", "category": "status",
"flags": [ "flags": [
"snatch" "snatch"
] ],
"effect": {
"name": "power_trick"
}
}, },
{ {
"name": "power_trip", "name": "power_trip",
@ -8070,7 +8168,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "power_trip"
}
}, },
{ {
"name": "power_up_punch", "name": "power_up_punch",
@ -8086,7 +8187,13 @@
"protect", "protect",
"mirror", "mirror",
"punch" "punch"
] ],
"effect": {
"name": "change_user_attack",
"parameters": {
"amount": 1
}
}
}, },
{ {
"name": "power_whip", "name": "power_whip",
@ -8102,6 +8209,7 @@
"protect", "protect",
"mirror" "mirror"
] ]
// No secondary effect
}, },
{ {
"name": "precipice_blades", "name": "precipice_blades",
@ -8117,6 +8225,7 @@
"mirror", "mirror",
"nonskybattle" "nonskybattle"
] ]
// No secondary effect
}, },
{ {
"name": "present", "name": "present",
@ -10909,7 +11018,10 @@
"contact", "contact",
"protect", "protect",
"mirror" "mirror"
] ],
"effect": {
"name": "thrash"
}
}, },
{ {
"name": "throat_chop", "name": "throat_chop",

View File

@ -8,7 +8,7 @@ public class MoveDataLoaderTests
[Test] [Test]
public async Task TestPrimaryMoveFile() 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(); var typeLibrary = new TypeLibrary();
typeLibrary.RegisterType("Normal"); typeLibrary.RegisterType("Normal");
typeLibrary.RegisterType("Fire"); typeLibrary.RegisterType("Fire");

View File

@ -13,7 +13,7 @@ public static class LibraryHelpers
var types = TypeDataLoader.LoadTypeLibrary(typesFile); var types = TypeDataLoader.LoadTypeLibrary(typesFile);
using var naturesFile = File.Open("Data/Natures.csv", FileMode.Open, FileAccess.Read, FileShare.Read); using var naturesFile = File.Open("Data/Natures.csv", FileMode.Open, FileAccess.Read, FileShare.Read);
var natures = NatureDataLoader.LoadNatureLibrary(naturesFile); 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); var moves = MoveDataLoader.LoadMoves(movesFile, types);
using var itemsFile = File.Open("Data/Items.json", FileMode.Open, FileAccess.Read, FileShare.Read); using var itemsFile = File.Open("Data/Items.json", FileMode.Open, FileAccess.Read, FileShare.Read);
var items = ItemDataLoader.LoadItems(itemsFile); 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); using var speciesFile = File.Open("Data/Pokemon.json", FileMode.Open, FileAccess.Read, FileShare.Read);
var species = SpeciesDataLoader.LoadSpecies(speciesFile, types); var species = SpeciesDataLoader.LoadSpecies(speciesFile, types);
var staticLibrary = new StaticLibraryImpl(new LibrarySettings() var staticLibrary = new StaticLibraryImpl(new LibrarySettings
{ {
MaxLevel = 100, MaxLevel = 100,
ShinyRate = 4096, ShinyRate = 4096,
}, species, moves, abilities, types, natures, growthRates, items); }, species, moves, abilities, types, natures, growthRates, items);
var dynamicLibrary = DynamicLibraryImpl.Create(staticLibrary, [ var dynamicLibrary = DynamicLibraryImpl.Create(staticLibrary, [
new Gen7Plugin(new Gen7PluginConfiguration() new Gen7Plugin(new Gen7PluginConfiguration
{ {
DamageCalculatorHasRandomness = false, DamageCalculatorHasRandomness = false,
}), }),

View File

@ -133,11 +133,12 @@ public class Gen7DamageCalculator(bool hasRandomness) : IDamageCalculator
if (bypassDefense) if (bypassDefense)
targetStats = target.FlatStats; targetStats = target.FlatStats;
var defensiveStat = targetStats.GetStatistic(defensive); var defensiveStat = targetStats.GetStatistic(defensive);
var origOffensiveStat = offensiveStat;
executingMove.RunScriptHook(script => executingMove.RunScriptHook(script =>
script.ChangeOffensiveStatValue(executingMove, target, hitNumber, ref offensiveStat)); script.ChangeOffensiveStatValue(executingMove, target, hitNumber, defensiveStat, ref offensiveStat));
executingMove.RunScriptHook(script => executingMove.RunScriptHook(script =>
script.ChangeDefensiveStatValue(executingMove, target, hitNumber, ref defensiveStat)); script.ChangeDefensiveStatValue(executingMove, target, hitNumber, origOffensiveStat, ref defensiveStat));
var modifier = (float)offensiveStat / defensiveStat; var modifier = (float)offensiveStat / defensiveStat;
executingMove.RunScriptHook(script => executingMove.RunScriptHook(script =>

View File

@ -6,7 +6,8 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
public class FoulPlay : Script public class FoulPlay : Script
{ {
/// <inheritdoc /> /// <inheritdoc />
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 value = move.UseMove.Category == MoveCategory.Physical
? target.BoostedStats.Attack ? target.BoostedStats.Attack

View File

@ -22,6 +22,9 @@ public class GuardSplit : Script
userStats.SetStatistic(Statistic.Defense, newDefense); userStats.SetStatistic(Statistic.Defense, newDefense);
userStats.SetStatistic(Statistic.SpecialDefense, newSpecialDefense); userStats.SetStatistic(Statistic.SpecialDefense, newSpecialDefense);
targetStats.SetStatistic(Statistic.Defense, newDefense);
targetStats.SetStatistic(Statistic.SpecialDefense, newSpecialDefense);
user.RecalculateFlatStats(); user.RecalculateFlatStats();
target.RecalculateFlatStats();
} }
} }

View File

@ -0,0 +1,28 @@
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
/// <summary>
/// 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.
/// </summary>
[Script(ScriptCategory.Move, "parting_shot")]
public class PartingShot : Script
{
/// <inheritdoc />
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);
}
}
}

View File

@ -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
}

View File

@ -0,0 +1,23 @@
using PkmnLib.Static;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
/// <summary>
/// Implements the secondary effect of Payback, which doubles the move's power if the user moves after the target.
/// </summary>
[Script(ScriptCategory.Move, "payback")]
public class Payback : Script
{
/// <inheritdoc />
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);
}
}
}

View File

@ -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;
/// <summary>
/// Implements the secondary effect of Perish Song, which causes all Pokemon on the field to faint in 3 turns.
/// </summary>
[Script(ScriptCategory.Move, "perish_song")]
public class PerishSong : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
if (move.User.Volatile.Contains<PerishSongEffect>())
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));
}
}
}

View File

@ -0,0 +1,26 @@
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
/// <summary>
/// Implements the secondary effect of Petal Dance, which forces the user to continue using the move for 2-3 turns,
/// then confuses the user.
/// </summary>
[Script(ScriptCategory.Move, "petal_dance")]
public class PetalDance : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
if (move.User.Volatile.Contains<PetalDanceEffect>())
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));
}
}

View File

@ -0,0 +1,36 @@
using System.Collections.Generic;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
/// <summary>
/// Implements the secondary effect of Phantom Force, which makes the user semi-invulnerable on the first turn
/// and attacks on the second turn.
/// </summary>
[Script(ScriptCategory.Move, "phantom_force")]
public class PhantomForce : Script
{
/// <inheritdoc />
public override void PreventMove(IExecutingMove move, ref bool prevent)
{
if (move.User.Volatile.Contains<PhantomForceCharge>())
return;
move.User.Volatile.Add(new PhantomForceCharge(move.User));
move.User.BattleData?.Battle.EventHook.Invoke(new DialogEvent("phantom_force_charge",
new Dictionary<string, object>
{
{ "user", move.User },
}));
prevent = true;
}
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
if (move.User.Volatile.Contains<PhantomForceCharge>())
return;
move.User.Volatile.Add(new PhantomForceCharge(move.User));
}
}

View File

@ -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
{
/// <inheritdoc />
public override void ChangeCriticalStage(IExecutingMove move, IPokemon target, byte hit, ref byte stage)
{
if (stage == 255)
return;
stage += 1;
}
/// <inheritdoc />
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<BadlyPoisoned>());
}
}
}

View File

@ -0,0 +1,39 @@
using PkmnLib.Static.Moves;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "pollen_puff")]
public class PollenPuff : Script
{
/// <inheritdoc />
/// <inheritdoc />
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;
}
}
/// <inheritdoc />
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);
}
}
}

View File

@ -0,0 +1,13 @@
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "powder")]
public class Powder : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
target.Volatile.Add(new PowderEffect());
}
}

View File

@ -0,0 +1,30 @@
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "power_split")]
public class PowerSplit : Script
{
/// <inheritdoc />
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();
}
}

View File

@ -0,0 +1,26 @@
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
[Script(ScriptCategory.Move, "power_swap")]
public class PowerSwap : Script
{
/// <inheritdoc />
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);
}
}

View File

@ -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
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
move.User.Volatile.Add(new PowerTrickEffect());
}
}

View File

@ -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
{
/// <inheritdoc />
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);
}
}

View File

@ -0,0 +1,26 @@
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
/// <summary>
/// Implements the secondary effect of Thrash, which forces the user to continue using the move for 2-3 turns,
/// then confuses the user.
/// </summary>
[Script(ScriptCategory.Move, "thrash")]
public class Thrash : Script
{
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
if (move.User.Volatile.Contains<ThrashEffect>())
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));
}
}

View File

@ -1,37 +1,10 @@
using PkmnLib.Plugin.Gen7.Scripts.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "outrage")] [Script(ScriptCategory.Pokemon, "outrage")]
public class OutrageEffect : Script public class OutrageEffect : OutrageLikeEffect
{ {
private readonly IPokemon _owner; public OutrageEffect(IPokemon owner, int turns, byte targetSide, byte targetPosition) : base(owner, turns,
private int _turns; targetSide, targetPosition, "outrage")
private readonly byte _targetSide;
private readonly byte _targetPosition;
public OutrageEffect(IPokemon owner, int turns, byte targetSide, byte targetPosition)
{ {
_owner = owner;
_turns = turns;
_targetSide = targetSide;
_targetPosition = targetPosition;
}
/// <inheritdoc />
public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice)
{
choice = TurnChoiceHelper.CreateMoveChoice(_owner, "outrage", _targetSide, _targetPosition);
}
/// <inheritdoc />
public override void OnAfterHits(IExecutingMove move, IPokemon target)
{
_turns--;
if (_turns <= 0)
{
RemoveSelf();
_owner.Volatile.Add(new Confusion());
}
} }
} }

View File

@ -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;
}
/// <inheritdoc />
public override void ForceTurnSelection(byte sideIndex, byte position, ref ITurnChoice? choice)
{
choice = TurnChoiceHelper.CreateMoveChoice(_owner, "_move", _targetSide, _targetPosition);
}
/// <inheritdoc />
public override void OnAfterHits(IExecutingMove move, IPokemon target)
{
_turns--;
if (_turns <= 0)
{
RemoveSelf();
_owner.Volatile.Add(new Confusion());
}
}
}

View File

@ -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;
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
_turns--;
if (_turns <= 0)
{
RemoveSelf();
_owner.Faint(DamageSource.Misc);
}
}
}

View File

@ -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")
{
}
}

View File

@ -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;
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
public override void BlockIncomingHit(IExecutingMove executingMove, IPokemon target, byte hitIndex, ref bool block)
{
block = true;
}
}

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "powder")]
public class PowderEffect : Script
{
/// <inheritdoc />
/// <inheritdoc />
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<string, object>
{
{ "user", executingMove.User },
{ "target", target },
}));
var health = executingMove.User.MaxHealth / 4;
executingMove.User.Damage(health, DamageSource.Misc);
block = true;
}
}
}

View File

@ -0,0 +1,19 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "power_trick")]
public class PowerTrickEffect : Script
{
/// <inheritdoc />
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
ref uint value)
{
value = defensiveStat;
}
/// <inheritdoc />
public override void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
ref uint value)
{
value = offensiveStat;
}
}

View File

@ -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")
{
}
}

View File

@ -0,0 +1,6 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Status;
[Script(ScriptCategory.Status, "badly_poisoned")]
public class BadlyPoisoned : Script
{
}