Even more abilities

This commit is contained in:
Deukhoofd 2025-06-09 14:23:51 +02:00
parent 97868ab4c6
commit 074f92bfc0
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
31 changed files with 319 additions and 51 deletions

View File

@ -23,7 +23,7 @@ public class ProxyScript : Script
/// <inheritdoc />
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
ImmutableStatisticSet<uint> targetStats, ref uint value)
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
foreach (var (script, handler) in _changeOffensiveStatValueEvents)
{

View File

@ -344,7 +344,7 @@ public abstract class Script : IDeepCloneable
/// This function allows a script to change the actual offensive stat values used when calculating damage
/// </summary>
public virtual void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
ImmutableStatisticSet<uint> targetStats, ref uint value)
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
}
@ -352,7 +352,7 @@ public abstract class Script : IDeepCloneable
/// This function allows a script to change the actual defensive stat values used when calculating damage.
/// </summary>
public virtual void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
ImmutableStatisticSet<uint> targetStats, ref uint value)
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
}

View File

@ -210,16 +210,36 @@
"fur_coat": {
"effect": "fur_coat"
},
"gale_wings": {},
"galvanize": {},
"gluttony": {},
"gooey": {},
"grass_pelt": {},
"grassy_surge": {},
"guts": {},
"harvest": {},
"healer": {},
"heatproof": {},
"gale_wings": {
"effect": "gale_wings"
},
"galvanize": {
"effect": "galvanize"
},
"gluttony": {
"effect": "gluttony"
},
"gooey": {
"effect": "gooey"
},
"grass_pelt": {
"effect": "grass_pelt"
},
"grassy_surge": {
"effect": "grassy_surge"
},
"guts": {
"effect": "guts"
},
"harvest": {
"effect": "harvest"
},
"healer": {
"effect": "healer"
},
"heatproof": {
"effect": "heatproof"
},
"heavy_metal": {},
"honey_gather": {},
"huge_power": {},

View File

@ -132,11 +132,10 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
var defensiveStat = targetStats.GetStatistic(defensive);
var origOffensiveStat = offensiveStat;
executingMove.RunScriptHook(script =>
script.ChangeOffensiveStatValue(executingMove, target, hitNumber, defensiveStat, targetStats,
ref offensiveStat));
executingMove.RunScriptHook(script => script.ChangeOffensiveStatValue(executingMove, target, hitNumber,
defensiveStat, targetStats, offensive, ref offensiveStat));
executingMove.RunScriptHook(script => script.ChangeDefensiveStatValue(executingMove, target, hitNumber,
origOffensiveStat, targetStats, ref defensiveStat));
origOffensiveStat, targetStats, defensive, ref defensiveStat));
var modifier = (float)offensiveStat / defensiveStat;
executingMove.RunScriptHook(script =>

View File

@ -1,5 +1,3 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>

View File

@ -1,5 +1,3 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>

View File

@ -1,5 +1,3 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>

View File

@ -1,5 +1,3 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>

View File

@ -1,5 +1,3 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>

View File

@ -1,5 +1,3 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>

View File

@ -1,5 +1,3 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>

View File

@ -0,0 +1,23 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Gale Wings is an ability that gives priority to Flying-type moves when the Pokémon's HP is full.
/// This ability is commonly associated with Fletchling, Fletchinder, and Talonflame.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Gale_Wings_(Ability)">Bulbapedia - Gale Wings</see>
/// </summary>
[Script(ScriptCategory.Ability, "gale_wings")]
public class GaleWings : Script
{
/// <inheritdoc />
public override void ChangePriority(IMoveChoice choice, ref sbyte priority)
{
if (choice.User.CurrentHealth != choice.User.MaxHealth)
return;
// Defensive programming, should never happen
if (priority == sbyte.MaxValue)
return;
priority++;
}
}

View File

@ -0,0 +1,29 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Galvanize is an ability that turns Normal-type moves into Electric-type moves and boosts their power.
/// This ability is exclusive to Alolan Golem.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Galvanize_(Ability)">Bulbapedia - Galvanize</see>
/// </summary>
[Script(ScriptCategory.Ability, "galvanize")]
public class Galvanize : Script
{
/// <inheritdoc />
public override void ChangeMoveType(IExecutingMove move, IPokemon target, byte hit,
ref TypeIdentifier? typeIdentifier)
{
if (typeIdentifier?.Name == "normal" &&
move.Battle.Library.StaticLibrary.Types.TryGetTypeIdentifier("electric", out var electricType))
{
typeIdentifier = electricType;
}
}
/// <inheritdoc />
public override void ChangeDamageModifier(IExecutingMove move, IPokemon target, byte hit, ref float modifier)
{
if (move.GetHitData(target, hit).Type?.Name == "electric")
modifier *= 1.2f;
}
}

View File

@ -0,0 +1,26 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Gluttony is an ability that causes a Pokémon to eat a held Berry when its HP drops to half or less, rather than a third or less.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Gluttony_(Ability)">Bulbapedia - Gluttony</see>
/// </summary>
[Script(ScriptCategory.Ability, "gluttony")]
public class Gluttony : Script
{
/// <inheritdoc />
public override void OnDamage(IPokemon pokemon, DamageSource source, uint oldHealth, uint newHealth)
{
if (pokemon.BattleData is null)
return;
var oldHealthFraction = (float)oldHealth / pokemon.MaxHealth;
var newHealthFraction = (float)newHealth / pokemon.MaxHealth;
if (!(oldHealthFraction >= 0.5f) || !(newHealthFraction < 0.5f))
return;
if (pokemon.HeldItem?.Category != ItemCategory.Berry)
return;
// FIXME: Implement after Berry triggers are implemented
}
}

View File

@ -0,0 +1,18 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Gooey is an ability that lowers the Speed of attackers making contact with the Pokémon.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Gooey_(Ability)">Bulbapedia - Gooey</see>
/// </summary>
[Script(ScriptCategory.Ability, "gooey")]
public class Gooey : Script
{
/// <inheritdoc />
public override void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit)
{
if (!move.UseMove.HasFlag("contact"))
return;
move.User.ChangeStatBoost(Statistic.Speed, -1, false, false);
}
}

View File

@ -0,0 +1,19 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Grass Pelt is an ability that boosts Defense in Grassy Terrain.
/// This ability is exclusive to Gogoat.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Grass_Pelt_(Ability)">Bulbapedia - Grass Pelt</see>
/// </summary>
[Script(ScriptCategory.Ability, "grass_pelt")]
public class GrassPelt : Script
{
/// <inheritdoc />
public override void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
if (move.Battle.TerrainName == ScriptUtils.ResolveName<Terrain.GrassyTerrain>() && stat == Statistic.Defense)
value = value.MultiplyOrMax(1.5f);
}
}

View File

@ -0,0 +1,25 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Grassy Surge is an ability that creates Grassy Terrain when the Pokémon enters battle.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Grassy_Surge_(Ability)">Bulbapedia - Grassy Surge</see>
/// </summary>
[Script(ScriptCategory.Ability, "grassy_surge")]
public class GrassySurge : Script
{
/// <inheritdoc />
public override void OnSwitchIn(IPokemon pokemon, byte position)
{
var battleData = pokemon.BattleData;
if (battleData == null)
return;
EventBatchId batchId = new();
battleData.Battle.EventHook.Invoke(new AbilityTriggerEvent(pokemon)
{
BatchId = batchId,
});
battleData.Battle.SetTerrain(ScriptUtils.ResolveName<Terrain.GrassyTerrain>(), batchId);
}
}

View File

@ -0,0 +1,23 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Guts is an ability that boosts the Attack stat if the Pokémon has a status condition.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Guts_(Ability)">Bulbapedia - Guts</see>
/// </summary>
[Script(ScriptCategory.Ability, "guts")]
public class Guts : Script
{
/// <inheritdoc />
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
if (target.StatusScript.IsEmpty)
return;
if (stat != Statistic.Attack)
return;
value = value.MultiplyOrMax(1.5f);
}
}

View File

@ -0,0 +1,39 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Harvest is an ability that may restore a consumed Berry at the end of each turn.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Harvest_(Ability)">Bulbapedia - Harvest</see>
/// </summary>
[Script(ScriptCategory.Ability, "harvest")]
public class Harvest : Script
{
private IPokemon? _pokemon;
/// <inheritdoc />
public override void OnAddedToParent(IScriptSource source)
{
if (source is not IPokemon pokemon)
throw new InvalidOperationException("Harvest can only be added to a Pokemon script source.");
_pokemon = pokemon;
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
if (_pokemon?.BattleData is null)
return;
if (_pokemon.HeldItem is not null)
return;
var consumedBerry = _pokemon.BattleData.ConsumedItems.FirstOrDefault(x => x.Category == ItemCategory.Berry);
if (consumedBerry != null)
{
var rng = battle.Random;
if (battle.WeatherName != ScriptUtils.ResolveName<Weather.HarshSunlight>() && rng.GetInt(1) != 0)
return;
battle.EventHook.Invoke(new AbilityTriggerEvent(_pokemon));
_ = _pokemon.SetHeldItem(consumedBerry);
}
}
}

View File

@ -0,0 +1,51 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Healer is an ability that may heal an ally's status condition at the end of each turn in a double battle.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Healer_(Ability)">Bulbapedia - Healer</see>
/// </summary>
[Script(ScriptCategory.Ability, "healer")]
public class Healer : Script
{
private IPokemon? _pokemon;
/// <inheritdoc />
public override void OnAddedToParent(IScriptSource source)
{
if (source is not IPokemon pokemon)
throw new InvalidOperationException("Harvest can only be added to a Pokemon script source.");
_pokemon = pokemon;
}
/// <inheritdoc />
public override void OnEndTurn(IBattle battle)
{
if (_pokemon?.BattleData is null)
return;
if (_pokemon.BattleData.Position > 0)
{
var leftAlly = _pokemon.BattleData.BattleSide.Pokemon[_pokemon.BattleData.Position - 1];
TryClearStatus(battle, leftAlly);
}
if (_pokemon.BattleData.Position < _pokemon.BattleData.BattleSide.Pokemon.Count - 1)
{
var rightAlly = _pokemon.BattleData.BattleSide.Pokemon[_pokemon.BattleData.Position + 1];
TryClearStatus(battle, rightAlly);
}
}
private static void TryClearStatus(IBattle battle, IPokemon? leftAlly)
{
if (leftAlly is null)
return;
if (leftAlly.StatusScript.IsEmpty)
return;
var rng = battle.Random;
if (rng.GetInt(3) != 0) // 1 in 3 chance to heal
return;
battle.EventHook.Invoke(new AbilityTriggerEvent(leftAlly));
leftAlly.ClearStatus();
}
}

View File

@ -0,0 +1,19 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Heatproof is an ability that halves the damage taken from Fire-type moves and burn.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Heatproof_(Ability)">Bulbapedia - Heatproof</see>
/// </summary>
[Script(ScriptCategory.Ability, "heatproof")]
public class Heatproof : Script
{
/// <inheritdoc />
public override void ChangeBasePower(IExecutingMove move, IPokemon target, byte hit, ref ushort basePower)
{
if (move.GetHitData(target, hit).Type?.Name == "fire")
{
basePower = (ushort)(basePower / 2);
}
}
}

View File

@ -1,5 +1,3 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
@ -15,10 +13,9 @@ public class IncreasedStab : Script
public override void ChangeStabModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber,
ref float modifier)
{
if (modifier.IsApproximatelyEqualTo(1.5f))
{
executingMove.Battle.EventHook.Invoke(new AbilityTriggerEvent(executingMove.User));
modifier = 2.0f;
}
if (!modifier.IsApproximatelyEqualTo(1.5f))
return;
executingMove.Battle.EventHook.Invoke(new AbilityTriggerEvent(executingMove.User));
modifier = 2.0f;
}
}

View File

@ -1,5 +1,3 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>

View File

@ -1,5 +1,3 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>

View File

@ -1,5 +1,3 @@
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>

View File

@ -8,7 +8,7 @@ public class WonderRoomEffect : Script
/// <inheritdoc />
public override void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
ImmutableStatisticSet<uint> targetStats, ref uint value)
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
value = move.UseMove.Category switch
{

View File

@ -7,7 +7,7 @@ public class FoulPlay : Script
{
/// <inheritdoc />
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint _,
ImmutableStatisticSet<uint> targetStats, ref uint value)
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
value = move.UseMove.Category == MoveCategory.Physical
? target.BoostedStats.Attack

View File

@ -5,7 +5,7 @@ public class Psyshock : Script
{
/// <inheritdoc />
public override void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
ImmutableStatisticSet<uint> targetStats, ref uint value)
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
value = targetStats.Defense;
}

View File

@ -5,7 +5,7 @@ public class FlashFireEffect : Script
{
/// <inheritdoc />
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
ImmutableStatisticSet<uint> targetStats, ref uint value)
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
if (move.GetHitData(target, hit).Type?.Name == "fire")
{

View File

@ -5,14 +5,14 @@ public class PowerTrickEffect : Script
{
/// <inheritdoc />
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
ImmutableStatisticSet<uint> targetStats, ref uint value)
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
value = defensiveStat;
}
/// <inheritdoc />
public override void ChangeDefensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint offensiveStat,
ImmutableStatisticSet<uint> targetStats, ref uint value)
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
value = offensiveStat;
}

View File

@ -21,7 +21,7 @@ public class FlowerGiftEffect : Script
/// <inheritdoc />
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
ImmutableStatisticSet<uint> targetStats, ref uint value)
ImmutableStatisticSet<uint> targetStats, Statistic stat, ref uint value)
{
if (move.Battle.WeatherName != ScriptUtils.ResolveName<Weather.HarshSunlight>())
return;