diff --git a/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs b/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs index cd73eee..044cec5 100644 --- a/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs +++ b/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs @@ -255,8 +255,11 @@ public static class MoveTurnExecutor if (secondaryEffect != null) { var preventSecondary = false; - target.RunScriptHook(x => + executingMove.RunScriptHook(x => x.PreventSecondaryEffect(executingMove, target, hitIndex, ref preventSecondary)); + target.RunScriptHook(x => + x.PreventIncomingSecondaryEffect(executingMove, target, hitIndex, + ref preventSecondary)); if (!preventSecondary) { diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs index 14dcdab..cbc0cf5 100644 --- a/PkmnLib.Dynamic/ScriptHandling/Script.cs +++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs @@ -450,12 +450,21 @@ public abstract class Script : IDeepCloneable { } + /// + /// This function allows a script to prevent a secondary effect of a move from being applied. + /// This means the move will still hit and do damage, but not trigger its secondary effect. Note that this + /// function is not called for status moves. + /// + public virtual void PreventSecondaryEffect(IExecutingMove move, IPokemon target, byte hit, ref bool prevent) + { + } + /// /// This function allows a script attached to a Pokemon or its parents to prevent an incoming /// secondary effect. This means the move will still hit and do damage, but not trigger its /// secondary effect. Note that this function is not called for status moves. /// - public virtual void PreventSecondaryEffect(IExecutingMove move, IPokemon target, byte hit, ref bool prevent) + public virtual void PreventIncomingSecondaryEffect(IExecutingMove move, IPokemon target, byte hit, ref bool prevent) { } diff --git a/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc b/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc index 6b869bc..2a1f2d8 100755 --- a/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc +++ b/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc @@ -502,31 +502,71 @@ "canBeChanged": false, "effect": "rks_system" }, - "rock_head": {}, - "rough_skin": {}, - "run_away": {}, - "sand_force": {}, - "sand_rush": {}, - "sand_stream": {}, - "sand_veil": {}, - "sap_sipper": {}, + "rock_head": { + "effect": "rock_head" + }, + "rough_skin": { + "effect": "rough_skin" + }, + "run_away": { + "effect": "run_away" + }, + "sand_force": { + "effect": "sand_force" + }, + "sand_rush": { + "effect": "sand_rush" + }, + "sand_stream": { + "effect": "sand_stream" + }, + "sand_veil": { + "effect": "sand_veil" + }, + "sap_sipper": { + "effect": "sap_sipper" + }, "schooling": { + "effect": "schooling", "canBeChanged": false }, - "scrappy": {}, - "serene_grace": {}, - "shadow_shield": {}, - "shadow_tag": {}, - "shed_skin": {}, - "sheer_force": {}, - "shell_armor": {}, - "shield_dust": {}, + "scrappy": { + "effect": "scrappy" + }, + "serene_grace": { + "effect": "serene_grace" + }, + "shadow_shield": { + "effect": "shadow_shield" + }, + "shadow_tag": { + "effect": "shadow_tag" + }, + "shed_skin": { + "effect": "shed_skin" + }, + "sheer_force": { + "effect": "sheer_force" + }, + "shell_armor": { + "effect": "shell_armor" + }, + "shield_dust": { + "effect": "shield_dust" + }, "shields_down": { + "effect": "shields_down", "canBeChanged": false }, - "simple": {}, - "skill_link": {}, - "slow_start": {}, + "simple": { + "effect": "simple" + }, + "skill_link": { + "effect": "skill_link" + }, + "slow_start": { + "effect": "slow_start" + }, "slush_rush": {}, "sniper": {}, "snow_cloak": {}, diff --git a/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json b/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json index 2b3a712..bb081fe 100755 --- a/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json +++ b/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json @@ -81395,7 +81395,7 @@ "eggCycles": 25, "flags": [], "formes": { - "blue": { + "default": { "abilities": [ "shields_down" ], @@ -81526,136 +81526,7 @@ } }, "blue-meteor": { - "abilities": [ - "shields_down" - ], - "hiddenAbilities": [], - "baseStats": { - "attack": 60, - "defense": 100, - "hp": 60, - "specialAttack": 60, - "specialDefense": 100, - "speed": 60 - }, - "evReward": { - "defense": 1, - "specialDefense": 1 - }, - "types": [ - "rock", - "flying" - ], - "height": 0.3, - "weight": 40, - "baseExp": 154, - "moves": { - "levelMoves": [ - { - "name": "tackle", - "level": 1 - }, - { - "name": "defense_curl", - "level": 3 - }, - { - "name": "rollout", - "level": 8 - }, - { - "name": "confuse_ray", - "level": 10 - }, - { - "name": "swift", - "level": 15 - }, - { - "name": "ancient_power", - "level": 17 - }, - { - "name": "self_destruct", - "level": 22 - }, - { - "name": "stealth_rock", - "level": 24 - }, - { - "name": "take_down", - "level": 29 - }, - { - "name": "autotomize", - "level": 31 - }, - { - "name": "cosmic_power", - "level": 36 - }, - { - "name": "power_gem", - "level": 38 - }, - { - "name": "double_edge", - "level": 43 - }, - { - "name": "shell_smash", - "level": 45 - }, - { - "name": "explosion", - "level": 50 - } - ], - "eggMoves": [], - "tutorMoves": [], - "machine": [ - "hyper_beam", - "solar_beam", - "earthquake", - "toxic", - "psychic", - "double_team", - "light_screen", - "reflect", - "explosion", - "rest", - "rock_slide", - "substitute", - "protect", - "sandstorm", - "swagger", - "attract", - "sleep_talk", - "return", - "frustration", - "safeguard", - "hidden_power", - "psych_up", - "facade", - "rock_tomb", - "calm_mind", - "gyro_ball", - "u_turn", - "rock_polish", - "giga_impact", - "stone_edge", - "charge_beam", - "round", - "acrobatics", - "bulldoze", - "confide", - "dazzling_gleam" - ], - "formeChange": [] - } - }, - "default": { + "isBattleOnly": true, "abilities": [ "shields_down" ], @@ -81916,6 +81787,7 @@ } }, "green-meteor": { + "isBattleOnly": true, "abilities": [ "shields_down" ], @@ -82176,6 +82048,7 @@ } }, "indigo-meteor": { + "isBattleOnly": true, "abilities": [ "shields_down" ], @@ -82436,6 +82309,7 @@ } }, "orange-meteor": { + "isBattleOnly": true, "abilities": [ "shields_down" ], @@ -82695,6 +82569,137 @@ "formeChange": [] } }, + "red-meteor": { + "isBattleOnly": true, + "abilities": [ + "shields_down" + ], + "hiddenAbilities": [], + "baseStats": { + "attack": 60, + "defense": 100, + "hp": 60, + "specialAttack": 60, + "specialDefense": 100, + "speed": 60 + }, + "evReward": { + "defense": 1, + "specialDefense": 1 + }, + "types": [ + "rock", + "flying" + ], + "height": 0.3, + "weight": 40, + "baseExp": 154, + "moves": { + "levelMoves": [ + { + "name": "tackle", + "level": 1 + }, + { + "name": "defense_curl", + "level": 3 + }, + { + "name": "rollout", + "level": 8 + }, + { + "name": "confuse_ray", + "level": 10 + }, + { + "name": "swift", + "level": 15 + }, + { + "name": "ancient_power", + "level": 17 + }, + { + "name": "self_destruct", + "level": 22 + }, + { + "name": "stealth_rock", + "level": 24 + }, + { + "name": "take_down", + "level": 29 + }, + { + "name": "autotomize", + "level": 31 + }, + { + "name": "cosmic_power", + "level": 36 + }, + { + "name": "power_gem", + "level": 38 + }, + { + "name": "double_edge", + "level": 43 + }, + { + "name": "shell_smash", + "level": 45 + }, + { + "name": "explosion", + "level": 50 + } + ], + "eggMoves": [], + "tutorMoves": [], + "machine": [ + "hyper_beam", + "solar_beam", + "earthquake", + "toxic", + "psychic", + "double_team", + "light_screen", + "reflect", + "explosion", + "rest", + "rock_slide", + "substitute", + "protect", + "sandstorm", + "swagger", + "attract", + "sleep_talk", + "return", + "frustration", + "safeguard", + "hidden_power", + "psych_up", + "facade", + "rock_tomb", + "calm_mind", + "gyro_ball", + "u_turn", + "rock_polish", + "giga_impact", + "stone_edge", + "charge_beam", + "round", + "acrobatics", + "bulldoze", + "confide", + "dazzling_gleam" + ], + "formeChange": [] + } + }, "violet": { "abilities": [ "shields_down" @@ -82826,6 +82831,7 @@ } }, "violet-meteor": { + "isBattleOnly": true, "abilities": [ "shields_down" ], @@ -83086,6 +83092,7 @@ } }, "yellow-meteor": { + "isBattleOnly": true, "abilities": [ "shields_down" ], @@ -141638,6 +141645,7 @@ } }, "school": { + "isBattleOnly": true, "abilities": [ "schooling" ], diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/RockHead.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/RockHead.cs new file mode 100644 index 0000000..023850f --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/RockHead.cs @@ -0,0 +1,21 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Rock Head is an ability that prevents the Pokémon from taking recoil damage from most moves. +/// +/// Bulbapedia - Rock Head +/// +[Script(ScriptCategory.Ability, "rock_head")] +public class RockHead : Script +{ + /// + public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args) + { + if (eventName != CustomTriggers.ModifyRecoil) + return; + if (args is CustomTriggers.ModifyRecoilArgs recoilArgs) + { + recoilArgs.Prevent = true; + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/RoughSkin.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/RoughSkin.cs new file mode 100644 index 0000000..95855fe --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/RoughSkin.cs @@ -0,0 +1,19 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Rough Skin is an ability that damages attackers using contact moves. +/// +/// Bulbapedia - Rough Skin +/// +[Script(ScriptCategory.Ability, "rough_skin")] +public class RoughSkin : Script +{ + /// + public override void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit) + { + if (move.GetHitData(target, hit).IsContact) + { + move.User.Damage(move.User.MaxHealth / 8, DamageSource.Misc); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/RunAway.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/RunAway.cs new file mode 100644 index 0000000..fe4e840 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/RunAway.cs @@ -0,0 +1,12 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Run Away is an ability that guarantees escape from wild battles. +/// +/// Bulbapedia - Run Away +/// +[Script(ScriptCategory.Ability, "run_away")] +public class RunAway : Script +{ + // No effect in battle +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandForce.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandForce.cs new file mode 100644 index 0000000..a6c83f0 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandForce.cs @@ -0,0 +1,26 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Sand Force is an ability that boosts the power of Rock, Ground, and Steel-type moves in a sandstorm. +/// +/// Bulbapedia - Sand Force +/// +[Script(ScriptCategory.Ability, "sand_force")] +public class SandForce : Script +{ + /// + public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower) + { + if (move.Battle.WeatherName == ScriptUtils.ResolveName()) + { + var type = move.GetHitData(target, hit).Type; + if (type != null && + (type.Value.Name == "rock" || type.Value.Name == "ground" || type.Value.Name == "steel")) + { + basePower = basePower.MultiplyOrMax(1.3f); + } + } + } + + // TODO: Prevent sandstorm damage. +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandRush.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandRush.cs new file mode 100644 index 0000000..c193dec --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandRush.cs @@ -0,0 +1,21 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Sand Rush is an ability that doubles the Pokémon's Speed during a sandstorm. +/// +/// Bulbapedia - Sand Rush +/// +[Script(ScriptCategory.Ability, "sand_rush")] +public class SandRush : Script +{ + /// + public override void ChangeSpeed(ITurnChoice choice, ref uint speed) + { + if (choice.User.BattleData?.Battle.WeatherName == ScriptUtils.ResolveName()) + { + speed = speed.MultiplyOrMax(2); + } + } + + // TODO: Prevent sandstorm damage. +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandStream.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandStream.cs new file mode 100644 index 0000000..2eaeb3f --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandStream.cs @@ -0,0 +1,25 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Sand Stream is an ability that creates a sandstorm when the Pokémon enters battle. +/// +/// Bulbapedia - Sand Stream +/// +[Script(ScriptCategory.Ability, "sand_stream")] +public class SandStream : Script +{ + /// + public override void OnSwitchIn(IPokemon pokemon, byte position) + { + var battleData = pokemon.BattleData; + if (battleData == null) + return; + + if (battleData.Battle.WeatherName == ScriptUtils.ResolveName()) + return; + + EventBatchId batchId = new(); + battleData.Battle.EventHook.Invoke(new AbilityTriggerEvent(pokemon)); + battleData.Battle.SetWeather(ScriptUtils.ResolveName(), 5, batchId); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandVeil.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandVeil.cs new file mode 100644 index 0000000..3b8ea70 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SandVeil.cs @@ -0,0 +1,22 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Sand Veil is an ability that raises the Pokémon's evasion during a sandstorm. +/// +/// Bulbapedia - Sand Veil +/// +[Script(ScriptCategory.Ability, "sand_veil")] +public class SandVeil : Script +{ + /// + public override void ChangeIncomingAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, + ref int modifiedAccuracy) + { + if (executingMove.Battle.WeatherName != ScriptUtils.ResolveName()) + return; + + modifiedAccuracy = (int)(modifiedAccuracy * (3277f / 4096f)); + } + + // TODO: Prevent sandstorm damage. +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SapSipper.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SapSipper.cs new file mode 100644 index 0000000..2cc4b3b --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SapSipper.cs @@ -0,0 +1,21 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Sap Sipper is an ability that grants immunity to Grass-type moves and raises Attack when hit by one. +/// +/// Bulbapedia - Sap Sipper +/// +[Script(ScriptCategory.Ability, "sap_sipper")] +public class SapSipper : Script +{ + /// + public override void IsInvulnerableToMove(IExecutingMove move, IPokemon target, ref bool invulnerable) + { + if (move.GetHitData(target, 0).Type?.Name == "grass") + { + invulnerable = true; + move.Battle.EventHook.Invoke(new AbilityTriggerEvent(target)); + target.ChangeStatBoost(Statistic.Attack, 1, true, false); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Schooling.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Schooling.cs new file mode 100644 index 0000000..d92de29 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Schooling.cs @@ -0,0 +1,45 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Schooling is an ability that changes Wishiwashi's form in battle. +/// +/// Bulbapedia - Schooling +/// +[Script(ScriptCategory.Ability, "schooling")] +public class Schooling : Script +{ + private IPokemon? _owningPokemon; + + /// + public override void OnAddedToParent(IScriptSource source) + { + if (source is not IPokemon pokemon) + throw new ArgumentException("Schooling script must be added to a Pokemon.", nameof(source)); + _owningPokemon = pokemon; + } + + /// + public override void OnSwitchIn(IPokemon pokemon, byte position) => ChangeFormIfNeeded(pokemon); + + /// + public override void OnEndTurn(IBattle battle) => ChangeFormIfNeeded(_owningPokemon); + + private static void ChangeFormIfNeeded(IPokemon? pokemon) + { + if (pokemon is null) + return; + + if (pokemon.Species.Name != "wishiwashi" || pokemon.BattleData?.Battle == null) + return; + // If Wishiwashi has less than 25% health, change to Solo form + if (pokemon.CurrentHealth < pokemon.MaxHealth / 4 && pokemon.Form.Name != "default") + { + pokemon.ChangeForm(pokemon.Species.GetDefaultForm()); + } + else if (pokemon.CurrentHealth >= pokemon.MaxHealth / 4 && pokemon.Form.Name != "school" && + pokemon.Species.TryGetForm("school", out var schoolForm)) + { + pokemon.ChangeForm(schoolForm); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Scrappy.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Scrappy.cs new file mode 100644 index 0000000..7e605cc --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Scrappy.cs @@ -0,0 +1,21 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Scrappy is an ability that allows the Pokémon to hit Ghost-type Pokémon with Normal- and Fighting-type moves. +/// +/// Bulbapedia - Scrappy +/// +[Script(ScriptCategory.Ability, "scrappy")] +public class Scrappy : Script +{ + /// + public override void ChangeTypesForMove(IExecutingMove executingMove, IPokemon target, byte hitIndex, + IList types) + { + var hitType = executingMove.GetHitData(target, hitIndex).Type; + if (hitType?.Name != "normal" && hitType?.Name != "fighting") + return; + if (types.Any(x => x.Name == "ghost")) + types.RemoveAll(x => x.Name == "ghost"); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SereneGrace.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SereneGrace.cs new file mode 100644 index 0000000..a87e172 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SereneGrace.cs @@ -0,0 +1,16 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Serene Grace is an ability that doubles the chance of additional effects occurring when attacking. +/// +/// Bulbapedia - Serene Grace +/// +[Script(ScriptCategory.Ability, "serene_grace")] +public class SereneGrace : Script +{ + /// + public override void ChangeEffectChance(IExecutingMove move, IPokemon target, byte hit, ref float chance) + { + chance *= 2; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShadowShield.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShadowShield.cs new file mode 100644 index 0000000..d4e339a --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShadowShield.cs @@ -0,0 +1,28 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Shadow Shield is an ability that reduces damage taken when at full HP. +/// +/// Bulbapedia - Shadow Shield +/// +[Script(ScriptCategory.Ability, "shadow_shield")] +public class ShadowShield : Script +{ + /// + public override void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage) + { + if (target.CurrentHealth == target.BoostedStats.Hp) + { + damage = (uint)(damage * 0.5); + } + } + + /// + public override void OnBeforeAnyHookInvoked(ref List? suppressedCategories) + { + // Shadow Shield can not be suppressed by any other script, so we remove it from the list of suppressed categories + // if it was added. + if (suppressedCategories != null && suppressedCategories.Contains(ScriptCategory.Ability)) + suppressedCategories.Remove(ScriptCategory.Ability); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShadowTag.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShadowTag.cs new file mode 100644 index 0000000..db60113 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShadowTag.cs @@ -0,0 +1,16 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Shadow Tag is an ability that prevents opposing Pokémon from fleeing or switching out. +/// +/// Bulbapedia - Shadow Tag +/// +[Script(ScriptCategory.Ability, "shadow_tag")] +public class ShadowTag : Script +{ + /// + public override void PreventOpponentRunAway(IFleeChoice choice, ref bool prevent) => prevent = true; + + /// + public override void PreventOpponentSwitch(ISwitchChoice choice, ref bool prevent) => prevent = true; +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShedSkin.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShedSkin.cs new file mode 100644 index 0000000..76db660 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShedSkin.cs @@ -0,0 +1,37 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Shed Skin is an ability that gives the Pokémon a chance to heal from a status condition at the end of each turn. +/// +/// Bulbapedia - Shed Skin +/// +[Script(ScriptCategory.Ability, "shed_skin")] +public class ShedSkin : Script +{ + private IPokemon? _owningPokemon; + + /// + public override void OnAddedToParent(IScriptSource source) + { + if (source is not IPokemon pokemon) + throw new ArgumentException("ShedSkin script must be added to a Pokemon.", nameof(source)); + _owningPokemon = pokemon; + } + + /// + public override void OnEndTurn(IBattle battle) + { + if (_owningPokemon is null || _owningPokemon.StatusScript.IsEmpty) + return; + + if (battle.Random.GetInt(3) == 0) + { + EventBatchId batchId = new(); + battle.EventHook.Invoke(new AbilityTriggerEvent(_owningPokemon) + { + BatchId = batchId, + }); + _owningPokemon.ClearStatus(batchId); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SheerForce.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SheerForce.cs new file mode 100644 index 0000000..25ef79b --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SheerForce.cs @@ -0,0 +1,22 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Sheer Force is an ability that increases the power of moves with secondary effects, but removes those effects. +/// +/// Bulbapedia - Sheer Force +/// +[Script(ScriptCategory.Ability, "sheer_force")] +public class SheerForce : Script +{ + /// + public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower) + { + basePower = basePower.MultiplyOrMax(5325f / 4096f); + } + + /// + public override void PreventSecondaryEffect(IExecutingMove move, IPokemon target, byte hit, ref bool prevent) + { + prevent = true; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShellArmor.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShellArmor.cs new file mode 100644 index 0000000..ccab711 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShellArmor.cs @@ -0,0 +1,16 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Shell Armor is an ability that prevents the Pokémon from receiving critical hits. +/// +/// Bulbapedia - Shell Armor +/// +[Script(ScriptCategory.Ability, "shell_armor")] +public class ShellArmor : Script +{ + /// + public override void BlockIncomingCriticalHit(IExecutingMove move, IPokemon target, byte hit, ref bool block) + { + block = true; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShieldDust.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShieldDust.cs new file mode 100644 index 0000000..c0321f3 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShieldDust.cs @@ -0,0 +1,17 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Shield Dust is an ability that blocks the additional effects of attacks taken. +/// +/// Bulbapedia - Shield Dust +/// +[Script(ScriptCategory.Ability, "shield_dust")] +public class ShieldDust : Script +{ + /// + public override void PreventIncomingSecondaryEffect(IExecutingMove move, IPokemon target, byte hit, + ref bool prevent) + { + prevent = true; + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShieldsDown.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShieldsDown.cs new file mode 100644 index 0000000..81dba86 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/ShieldsDown.cs @@ -0,0 +1,57 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Shields Down is an ability that changes Minior's form depending on its HP. +/// +/// Bulbapedia - Shields Down +/// +[Script(ScriptCategory.Ability, "shields_down")] +public class ShieldsDown : Script +{ + private IPokemon? _owningPokemon; + + /// + public override void OnAddedToParent(IScriptSource source) + { + if (source is not IPokemon pokemon) + throw new ArgumentException("ShieldsDown script must be added to a Pokemon.", nameof(source)); + _owningPokemon = pokemon; + } + + /// + public override void OnSwitchIn(IPokemon pokemon, byte position) => ChangeFormIfNeeded(pokemon); + + /// + public override void OnEndTurn(IBattle battle) => ChangeFormIfNeeded(_owningPokemon); + + private static void ChangeFormIfNeeded(IPokemon? pokemon) + { + if (pokemon is null) + return; + + if (pokemon.Species.Name != "minior" || pokemon.BattleData?.Battle == null) + return; + if (pokemon.CurrentHealth < pokemon.MaxHealth / 2 && pokemon.Form.Name.ToString().EndsWith("-meteor")) + { + var coreForm = pokemon.Form.Name.ToString().Replace("-meteor", ""); + if (coreForm == "blue") + coreForm = "default"; + if (pokemon.Species.TryGetForm(coreForm, out var coreFormData)) + { + pokemon.ChangeForm(coreFormData); + } + } + else if (pokemon.CurrentHealth >= pokemon.MaxHealth / 2 && + pokemon.Form.Name.ToString().EndsWith("-meteor") == false) + { + var baseFormName = pokemon.Form.Name; + if (baseFormName == "default") + baseFormName = "blue"; + var meteorFormName = baseFormName + "-meteor"; + if (pokemon.Species.TryGetForm(meteorFormName, out var meteorFormData)) + { + pokemon.ChangeForm(meteorFormData); + } + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Simple.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Simple.cs new file mode 100644 index 0000000..35535f7 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Simple.cs @@ -0,0 +1,16 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Simple is an ability that doubles the effect of stat changes. +/// +/// Bulbapedia - Simple +/// +[Script(ScriptCategory.Ability, "simple")] +public class Simple : Script +{ + /// + public override void ChangeStatBoostChange(IPokemon target, Statistic stat, bool selfInflicted, ref sbyte amount) + { + amount = amount.MultiplyOrMax(2); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SkillLink.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SkillLink.cs new file mode 100644 index 0000000..d912360 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SkillLink.cs @@ -0,0 +1,21 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Skill Link is an ability that makes multi-strike moves always hit the maximum number of times. +/// +/// Bulbapedia - Skill Link +/// +[Script(ScriptCategory.Ability, "skill_link")] +public class SkillLink : Script +{ + /// + public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args) + { + if (eventName != CustomTriggers.Modify2_5HitMove) + return; + if (args is CustomTriggers.Modify2_5HitMoveArgs triggerArgs) + { + triggerArgs.NumberOfHits = 5; + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SlowStart.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SlowStart.cs new file mode 100644 index 0000000..8532a5b --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/SlowStart.cs @@ -0,0 +1,27 @@ +using PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +namespace PkmnLib.Plugin.Gen7.Scripts.Abilities; + +/// +/// Slow Start is an ability that halves the user's Attack and Speed for five turns after entering battle. +/// +/// Bulbapedia - Slow Start +/// +[Script(ScriptCategory.Ability, "slow_start")] +public class SlowStart : Script +{ + private IPokemon? _pokemon; + + /// + public override void OnSwitchIn(IPokemon pokemon, byte position) + { + _pokemon = pokemon; + pokemon.Volatile.Add(new SlowStartEffect()); + } + + /// + public override void OnRemove() + { + _pokemon?.Volatile.Remove(); + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs index 4c7d6d6..504964a 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs @@ -116,4 +116,20 @@ public static class CustomTriggers public uint Damage { get; set; } = Damage; public bool Invert { get; set; } = false; } + + public static readonly StringKey ModifyRecoil = "modify_recoil"; + + public record ModifyRecoilArgs(IExecutingMove Move, IPokemon Target, byte Hit, uint Damage, uint Recoil) + : ICustomTriggerArgs + { + public uint Recoil { get; set; } = Recoil; + public bool Prevent { get; set; } = false; + } + + public static readonly StringKey Modify2_5HitMove = "modify_2_5_hit_move"; + + public record Modify2_5HitMoveArgs(IMoveChoice moveChoice, byte NumberOfHits) : ICustomTriggerArgs + { + public byte NumberOfHits { get; set; } = NumberOfHits; + } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FlareBlitz.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FlareBlitz.cs index 719d10d..dd08175 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FlareBlitz.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/FlareBlitz.cs @@ -10,13 +10,19 @@ public class FlareBlitz : Script if (battleData == null) return; + var hitData = move.GetHitData(target, hit); + var recoilDamage = hitData.Damage * (1 / 3); + + var triggerArgs = new CustomTriggers.ModifyRecoilArgs(move, target, hit, hitData.Damage, recoilDamage); + move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyRecoil, triggerArgs)); + if (triggerArgs.Prevent) + return; + if (battleData.Battle.Random.EffectChance(10, move, target, hit)) { target.SetStatus("burned", false); } - var hitData = move.GetHitData(target, hit); - var recoilDamage = hitData.Damage * (1 / 3); move.User.Damage(recoilDamage, DamageSource.Misc); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MultiHitMove.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MultiHitMove.cs index 0e11465..130665f 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MultiHitMove.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/MultiHitMove.cs @@ -19,6 +19,10 @@ public class MultiHitMove : Script < 85 => 4, _ => 5, }; + var triggerArgs = new CustomTriggers.Modify2_5HitMoveArgs(choice, (byte)newHits); + choice.RunScriptHook(x => x.CustomTrigger(CustomTriggers.Modify2_5HitMove, triggerArgs)); + newHits = triggerArgs.NumberOfHits; + numberOfHits = (byte)newHits; } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Recoil.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Recoil.cs index 3baca7e..eca38a4 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Recoil.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Moves/Recoil.cs @@ -1,5 +1,3 @@ -using PkmnLib.Static.Utils; - namespace PkmnLib.Plugin.Gen7.Scripts.Moves; [Script(ScriptCategory.Move, "recoil")] @@ -23,6 +21,12 @@ public class Recoil : Script return; var hitData = move.GetHitData(target, hit); var recoilDamage = (uint)(hitData.Damage * _recoilPercentage); + + var triggerArgs = new CustomTriggers.ModifyRecoilArgs(move, target, hit, hitData.Damage, recoilDamage); + move.RunScriptHook(x => x.CustomTrigger(CustomTriggers.ModifyRecoil, triggerArgs)); + if (triggerArgs.Prevent) + return; + move.User.Damage(recoilDamage, DamageSource.Misc); } } \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SlowStartEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SlowStartEffect.cs new file mode 100644 index 0000000..8d264f2 --- /dev/null +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/SlowStartEffect.cs @@ -0,0 +1,33 @@ +namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon; + +[Script(ScriptCategory.Pokemon, "slow_start")] +public class SlowStartEffect : Script +{ + private int _turnsRemaining = 5; + + /// + public override void ChangeSpeed(ITurnChoice choice, ref uint speed) + { + speed /= 2; + } + + /// + public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat, + ImmutableStatisticSet targetStats, Statistic stat, ref uint value) + { + if (stat == Statistic.Attack) + value /= 2; + } + + /// + public override void OnEndTurn(IBattle battle) + { + if (_turnsRemaining <= 0) + return; + _turnsRemaining--; + if (_turnsRemaining == 0) + { + RemoveSelf(); + } + } +} \ No newline at end of file diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/AromaVeilEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/AromaVeilEffect.cs index f93825f..3a57889 100644 --- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/AromaVeilEffect.cs +++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Side/AromaVeilEffect.cs @@ -24,7 +24,8 @@ public class AromaVeilEffect : Script } /// - public override void PreventSecondaryEffect(IExecutingMove move, IPokemon target, byte hit, ref bool prevent) + public override void PreventIncomingSecondaryEffect(IExecutingMove move, IPokemon target, byte hit, + ref bool prevent) { if (move.UseMove.HasFlag("mental")) prevent = true;