Even more abilities
All checks were successful
Build / Build (push) Successful in 51s

This commit is contained in:
2025-06-14 13:21:23 +02:00
parent 4b07f36176
commit 5961bb746e
31 changed files with 787 additions and 157 deletions

View File

@@ -0,0 +1,21 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Rock Head is an ability that prevents the Pokémon from taking recoil damage from most moves.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Rock_Head_(Ability)">Bulbapedia - Rock Head</see>
/// </summary>
[Script(ScriptCategory.Ability, "rock_head")]
public class RockHead : Script
{
/// <inheritdoc />
public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
{
if (eventName != CustomTriggers.ModifyRecoil)
return;
if (args is CustomTriggers.ModifyRecoilArgs recoilArgs)
{
recoilArgs.Prevent = true;
}
}
}

View File

@@ -0,0 +1,19 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Rough Skin is an ability that damages attackers using contact moves.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Rough_Skin_(Ability)">Bulbapedia - Rough Skin</see>
/// </summary>
[Script(ScriptCategory.Ability, "rough_skin")]
public class RoughSkin : Script
{
/// <inheritdoc />
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);
}
}
}

View File

@@ -0,0 +1,12 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Run Away is an ability that guarantees escape from wild battles.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Run_Away_(Ability)">Bulbapedia - Run Away</see>
/// </summary>
[Script(ScriptCategory.Ability, "run_away")]
public class RunAway : Script
{
// No effect in battle
}

View File

@@ -0,0 +1,26 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Sand Force is an ability that boosts the power of Rock, Ground, and Steel-type moves in a sandstorm.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Sand_Force_(Ability)">Bulbapedia - Sand Force</see>
/// </summary>
[Script(ScriptCategory.Ability, "sand_force")]
public class SandForce : Script
{
/// <inheritdoc />
public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
{
if (move.Battle.WeatherName == ScriptUtils.ResolveName<Weather.Sandstorm>())
{
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.
}

View File

@@ -0,0 +1,21 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Sand Rush is an ability that doubles the Pokémon's Speed during a sandstorm.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Sand_Rush_(Ability)">Bulbapedia - Sand Rush</see>
/// </summary>
[Script(ScriptCategory.Ability, "sand_rush")]
public class SandRush : Script
{
/// <inheritdoc />
public override void ChangeSpeed(ITurnChoice choice, ref uint speed)
{
if (choice.User.BattleData?.Battle.WeatherName == ScriptUtils.ResolveName<Weather.Sandstorm>())
{
speed = speed.MultiplyOrMax(2);
}
}
// TODO: Prevent sandstorm damage.
}

View File

@@ -0,0 +1,25 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Sand Stream is an ability that creates a sandstorm when the Pokémon enters battle.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Sand_Stream_(Ability)">Bulbapedia - Sand Stream</see>
/// </summary>
[Script(ScriptCategory.Ability, "sand_stream")]
public class SandStream : Script
{
/// <inheritdoc />
public override void OnSwitchIn(IPokemon pokemon, byte position)
{
var battleData = pokemon.BattleData;
if (battleData == null)
return;
if (battleData.Battle.WeatherName == ScriptUtils.ResolveName<Weather.Sandstorm>())
return;
EventBatchId batchId = new();
battleData.Battle.EventHook.Invoke(new AbilityTriggerEvent(pokemon));
battleData.Battle.SetWeather(ScriptUtils.ResolveName<Weather.Sandstorm>(), 5, batchId);
}
}

View File

@@ -0,0 +1,22 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Sand Veil is an ability that raises the Pokémon's evasion during a sandstorm.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Sand_Veil_(Ability)">Bulbapedia - Sand Veil</see>
/// </summary>
[Script(ScriptCategory.Ability, "sand_veil")]
public class SandVeil : Script
{
/// <inheritdoc />
public override void ChangeIncomingAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex,
ref int modifiedAccuracy)
{
if (executingMove.Battle.WeatherName != ScriptUtils.ResolveName<Weather.Sandstorm>())
return;
modifiedAccuracy = (int)(modifiedAccuracy * (3277f / 4096f));
}
// TODO: Prevent sandstorm damage.
}

View File

@@ -0,0 +1,21 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Sap Sipper is an ability that grants immunity to Grass-type moves and raises Attack when hit by one.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Sap_Sipper_(Ability)">Bulbapedia - Sap Sipper</see>
/// </summary>
[Script(ScriptCategory.Ability, "sap_sipper")]
public class SapSipper : Script
{
/// <inheritdoc />
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);
}
}
}

View File

@@ -0,0 +1,45 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Schooling is an ability that changes Wishiwashi's form in battle.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Schooling_(Ability)">Bulbapedia - Schooling</see>
/// </summary>
[Script(ScriptCategory.Ability, "schooling")]
public class Schooling : Script
{
private IPokemon? _owningPokemon;
/// <inheritdoc />
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;
}
/// <inheritdoc />
public override void OnSwitchIn(IPokemon pokemon, byte position) => ChangeFormIfNeeded(pokemon);
/// <inheritdoc />
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);
}
}
}

View File

@@ -0,0 +1,21 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Scrappy is an ability that allows the Pokémon to hit Ghost-type Pokémon with Normal- and Fighting-type moves.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Scrappy_(Ability)">Bulbapedia - Scrappy</see>
/// </summary>
[Script(ScriptCategory.Ability, "scrappy")]
public class Scrappy : Script
{
/// <inheritdoc />
public override void ChangeTypesForMove(IExecutingMove executingMove, IPokemon target, byte hitIndex,
IList<TypeIdentifier> 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");
}
}

View File

@@ -0,0 +1,16 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Serene Grace is an ability that doubles the chance of additional effects occurring when attacking.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Serene_Grace_(Ability)">Bulbapedia - Serene Grace</see>
/// </summary>
[Script(ScriptCategory.Ability, "serene_grace")]
public class SereneGrace : Script
{
/// <inheritdoc />
public override void ChangeEffectChance(IExecutingMove move, IPokemon target, byte hit, ref float chance)
{
chance *= 2;
}
}

View File

@@ -0,0 +1,28 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Shadow Shield is an ability that reduces damage taken when at full HP.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Shadow_Shield_(Ability)">Bulbapedia - Shadow Shield</see>
/// </summary>
[Script(ScriptCategory.Ability, "shadow_shield")]
public class ShadowShield : Script
{
/// <inheritdoc />
public override void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{
if (target.CurrentHealth == target.BoostedStats.Hp)
{
damage = (uint)(damage * 0.5);
}
}
/// <inheritdoc />
public override void OnBeforeAnyHookInvoked(ref List<ScriptCategory>? 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);
}
}

View File

@@ -0,0 +1,16 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Shadow Tag is an ability that prevents opposing Pokémon from fleeing or switching out.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Shadow_Tag_(Ability)">Bulbapedia - Shadow Tag</see>
/// </summary>
[Script(ScriptCategory.Ability, "shadow_tag")]
public class ShadowTag : Script
{
/// <inheritdoc />
public override void PreventOpponentRunAway(IFleeChoice choice, ref bool prevent) => prevent = true;
/// <inheritdoc />
public override void PreventOpponentSwitch(ISwitchChoice choice, ref bool prevent) => prevent = true;
}

View File

@@ -0,0 +1,37 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Shed Skin is an ability that gives the Pokémon a chance to heal from a status condition at the end of each turn.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Shed_Skin_(Ability)">Bulbapedia - Shed Skin</see>
/// </summary>
[Script(ScriptCategory.Ability, "shed_skin")]
public class ShedSkin : Script
{
private IPokemon? _owningPokemon;
/// <inheritdoc />
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;
}
/// <inheritdoc />
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);
}
}
}

View File

@@ -0,0 +1,22 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Sheer Force is an ability that increases the power of moves with secondary effects, but removes those effects.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Sheer_Force_(Ability)">Bulbapedia - Sheer Force</see>
/// </summary>
[Script(ScriptCategory.Ability, "sheer_force")]
public class SheerForce : Script
{
/// <inheritdoc />
public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
{
basePower = basePower.MultiplyOrMax(5325f / 4096f);
}
/// <inheritdoc />
public override void PreventSecondaryEffect(IExecutingMove move, IPokemon target, byte hit, ref bool prevent)
{
prevent = true;
}
}

View File

@@ -0,0 +1,16 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Shell Armor is an ability that prevents the Pokémon from receiving critical hits.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Shell_Armor_(Ability)">Bulbapedia - Shell Armor</see>
/// </summary>
[Script(ScriptCategory.Ability, "shell_armor")]
public class ShellArmor : Script
{
/// <inheritdoc />
public override void BlockIncomingCriticalHit(IExecutingMove move, IPokemon target, byte hit, ref bool block)
{
block = true;
}
}

View File

@@ -0,0 +1,17 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Shield Dust is an ability that blocks the additional effects of attacks taken.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Shield_Dust_(Ability)">Bulbapedia - Shield Dust</see>
/// </summary>
[Script(ScriptCategory.Ability, "shield_dust")]
public class ShieldDust : Script
{
/// <inheritdoc />
public override void PreventIncomingSecondaryEffect(IExecutingMove move, IPokemon target, byte hit,
ref bool prevent)
{
prevent = true;
}
}

View File

@@ -0,0 +1,57 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Shields Down is an ability that changes Minior's form depending on its HP.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Shields_Down_(Ability)">Bulbapedia - Shields Down</see>
/// </summary>
[Script(ScriptCategory.Ability, "shields_down")]
public class ShieldsDown : Script
{
private IPokemon? _owningPokemon;
/// <inheritdoc />
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;
}
/// <inheritdoc />
public override void OnSwitchIn(IPokemon pokemon, byte position) => ChangeFormIfNeeded(pokemon);
/// <inheritdoc />
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);
}
}
}
}

View File

@@ -0,0 +1,16 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Simple is an ability that doubles the effect of stat changes.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Simple_(Ability)">Bulbapedia - Simple</see>
/// </summary>
[Script(ScriptCategory.Ability, "simple")]
public class Simple : Script
{
/// <inheritdoc />
public override void ChangeStatBoostChange(IPokemon target, Statistic stat, bool selfInflicted, ref sbyte amount)
{
amount = amount.MultiplyOrMax(2);
}
}

View File

@@ -0,0 +1,21 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Skill Link is an ability that makes multi-strike moves always hit the maximum number of times.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Skill_Link_(Ability)">Bulbapedia - Skill Link</see>
/// </summary>
[Script(ScriptCategory.Ability, "skill_link")]
public class SkillLink : Script
{
/// <inheritdoc />
public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
{
if (eventName != CustomTriggers.Modify2_5HitMove)
return;
if (args is CustomTriggers.Modify2_5HitMoveArgs triggerArgs)
{
triggerArgs.NumberOfHits = 5;
}
}
}

View File

@@ -0,0 +1,27 @@
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Slow Start is an ability that halves the user's Attack and Speed for five turns after entering battle.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Slow_Start_(Ability)">Bulbapedia - Slow Start</see>
/// </summary>
[Script(ScriptCategory.Ability, "slow_start")]
public class SlowStart : Script
{
private IPokemon? _pokemon;
/// <inheritdoc />
public override void OnSwitchIn(IPokemon pokemon, byte position)
{
_pokemon = pokemon;
pokemon.Volatile.Add(new SlowStartEffect());
}
/// <inheritdoc />
public override void OnRemove()
{
_pokemon?.Volatile.Remove<SlowStartEffect>();
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,33 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "slow_start")]
public class SlowStartEffect : Script
{
private int _turnsRemaining = 5;
/// <inheritdoc />
public override void ChangeSpeed(ITurnChoice choice, ref uint speed)
{
speed /= 2;
}
/// <inheritdoc />
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
if (stat == Statistic.Attack)
value /= 2;
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
if (_turnsRemaining <= 0)
return;
_turnsRemaining--;
if (_turnsRemaining == 0)
{
RemoveSelf();
}
}
}

View File

@@ -24,7 +24,8 @@ public class AromaVeilEffect : Script
}
/// <inheritdoc />
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;