More abilities
All checks were successful
Build / Build (push) Successful in 48s

This commit is contained in:
Deukhoofd 2025-06-09 13:44:26 +02:00
parent 00005aa4bf
commit 97868ab4c6
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
94 changed files with 829 additions and 150 deletions

View File

@ -1,5 +1,6 @@
using PkmnLib.Dynamic.Models;
using PkmnLib.Static.Species;
using PkmnLib.Static.Utils;
namespace PkmnLib.Dynamic.Events;
@ -18,6 +19,8 @@ public record AbilityTriggerEvent : IEventData
/// </summary>
public IAbility? Ability { get; }
public Dictionary<StringKey, object?>? Metadata { get; init; } = null;
/// <inheritdoc cref="AbilityTriggerEvent"/>
public AbilityTriggerEvent(IPokemon pokemon)
{

View File

@ -274,8 +274,9 @@ public interface IPokemon : IScriptSource, IDeepCloneable
/// <param name="stat">The stat to be changed</param>
/// <param name="change">The amount to change the stat by</param>
/// <param name="selfInflicted">Whether the change was self-inflicted. This can be relevant in scripts.</param>
/// <param name="force"></param>
/// <param name="batchId">The event batch ID this change is a part of. This is relevant for visual handling</param>
bool ChangeStatBoost(Statistic stat, sbyte change, bool selfInflicted, EventBatchId batchId = default);
bool ChangeStatBoost(Statistic stat, sbyte change, bool selfInflicted, bool force, EventBatchId batchId = default);
/// <summary>
/// Suppresses the ability of the Pokémon.
@ -354,7 +355,7 @@ public interface IPokemon : IScriptSource, IDeepCloneable
/// <summary>
/// Adds a non-volatile status to the Pokemon.
/// </summary>
bool SetStatus(StringKey status, EventBatchId batchId = default);
bool SetStatus(StringKey status, bool selfInflicted, EventBatchId batchId = default);
/// <summary>
/// Removes the current non-volatile status from the Pokemon.
@ -841,15 +842,20 @@ public class PokemonImpl : ScriptSource, IPokemon
}
/// <inheritdoc />
public bool ChangeStatBoost(Statistic stat, sbyte change, bool selfInflicted, EventBatchId batchId = default)
public bool ChangeStatBoost(Statistic stat, sbyte change, bool selfInflicted, bool force,
EventBatchId batchId = default)
{
if (!force)
{
var prevented = false;
this.RunScriptHook(script => script.PreventStatBoostChange(this, stat, change, selfInflicted, ref prevented));
this.RunScriptHook(script =>
script.PreventStatBoostChange(this, stat, change, selfInflicted, ref prevented));
if (prevented)
return false;
this.RunScriptHook(script => script.ChangeStatBoostChange(this, stat, selfInflicted, ref change));
if (change == 0)
return false;
}
var changed = false;
var oldBoost = StatBoost.GetStatistic(stat);
changed = change switch
@ -1135,7 +1141,7 @@ public class PokemonImpl : ScriptSource, IPokemon
public bool HasStatus(StringKey status) => StatusScript.Script?.Name == status;
/// <inheritdoc />
public bool SetStatus(StringKey status, EventBatchId batchId = default)
public bool SetStatus(StringKey status, bool selfInflicted, EventBatchId batchId = default)
{
if (!Library.ScriptResolver.TryResolve(ScriptCategory.Status, status, null, out var statusScript))
throw new KeyNotFoundException($"Status script {status} not found");
@ -1145,7 +1151,7 @@ public class PokemonImpl : ScriptSource, IPokemon
var oldStatus = StatusScript.Script?.Name;
var preventStatus = false;
this.RunScriptHook(script => script.PreventStatusChange(this, status, ref preventStatus));
this.RunScriptHook(script => script.PreventStatusChange(this, status, selfInflicted, ref preventStatus));
if (preventStatus)
return false;

View File

@ -0,0 +1,36 @@
using PkmnLib.Dynamic.Models;
using PkmnLib.Static;
namespace PkmnLib.Dynamic.ScriptHandling;
public class ProxyScript : Script
{
public delegate void ChangeOffensiveStatValueEventHandler(IExecutingMove move, IPokemon target, byte hit,
uint defensiveStat, ImmutableStatisticSet<uint> targetStats, ref uint value);
private readonly List<(Script, ChangeOffensiveStatValueEventHandler)> _changeOffensiveStatValueEvents = new();
public void AddChangeOffensiveStatValueEvent(Script script, ChangeOffensiveStatValueEventHandler handler)
{
_changeOffensiveStatValueEvents.Add((script, handler));
script.OnRemoveEvent += OnRemoveScriptEvent;
}
private void OnRemoveScriptEvent(Script script)
{
_changeOffensiveStatValueEvents.RemoveAll(x => x.Item1 == script);
}
/// <inheritdoc />
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
ImmutableStatisticSet<uint> targetStats, ref uint value)
{
foreach (var (script, handler) in _changeOffensiveStatValueEvents)
{
if (!script.IsSuppressed)
{
handler(move, target, hit, defensiveStat, targetStats, ref value);
}
}
}
}

View File

@ -728,7 +728,8 @@ public abstract class Script : IDeepCloneable
/// <summary>
/// This function allows a script to prevent a Pokemon from being affected by a status condition.
/// </summary>
public virtual void PreventStatusChange(IPokemon pokemonImpl, StringKey status, ref bool preventStatus)
public virtual void PreventStatusChange(IPokemon pokemon, StringKey status, bool selfInflicted,
ref bool preventStatus)
{
}

View File

@ -65,10 +65,7 @@ public class ScriptContainer : IReadOnlyScriptContainer
/// </summary>
public Script? Clear()
{
if (Script is not null)
{
Script.OnRemove();
}
Script?.OnRemove();
var script = Script;
Script = null;
return script;

View File

@ -1,4 +1,5 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using PkmnLib.Dynamic.ScriptHandling.Registry;
using PkmnLib.Static.Utils;
@ -31,6 +32,11 @@ public interface IScriptSet : IEnumerable<ScriptContainer>
/// </summary>
ScriptContainer? Get(StringKey scriptKey);
/// <summary>
/// Tries to get a script from the set using its type.
/// </summary>
bool TryGet<T>([NotNullWhen(true)] out T? script) where T : Script;
/// <summary>
/// Gets a script from the set using its type.
/// </summary>
@ -144,6 +150,20 @@ public class ScriptSet : IScriptSet
/// <inheritdoc />
public ScriptContainer? Get(StringKey scriptKey) => _scripts.FirstOrDefault(s => s.Script?.Name == scriptKey);
/// <inheritdoc />
public bool TryGet<T>([NotNullWhen(true)] out T? script) where T : Script
{
var scriptName = ScriptUtils.ResolveName<T>();
var container = _scripts.FirstOrDefault(sc => sc.Script?.Name == scriptName);
if (container?.Script is not T s)
{
script = null;
return false;
}
script = s;
return true;
}
/// <inheritdoc />
public T? Get<T>() where T : Script => Get(ScriptUtils.ResolveName<T>())?.Script as T;

View File

@ -111,7 +111,7 @@ public class DeepCloneTests
using var battle = new BattleImpl(library, parties, false, 2, 3, false, 0);
battle.Sides[0].SwapPokemon(0, party1[0]);
battle.Sides[1].SwapPokemon(0, party2[0]);
party1[0]!.ChangeStatBoost(Statistic.Defense, 2, true);
party1[0]!.ChangeStatBoost(Statistic.Defense, 2, true, false);
await Assert.That(party1[0]!.StatBoost.Defense).IsEqualTo((sbyte)2);
var clone = battle.DeepClone();

View File

@ -180,24 +180,36 @@
"flare_boost": {
"effect": "flare_boost"
},
"flash_fire": {},
"flash_fire": {
"effect": "flash_fire"
},
"flower_gift": {
"flags": [
"cant_be_copied"
]
"effect": "flower_gift"
},
"flower_veil": {
"effect": "flower_veil"
},
"fluffy": {
"effect": "fluffy"
},
"flower_veil": {},
"fluffy": {},
"forecast": {
"flags": [
"cant_be_copied"
]
"effect": "forecast"
},
"forewarn": {
"effect": "forewarn"
},
"friend_guard": {
"effect": "friend_guard"
},
"frisk": {
"effect": "frisk"
},
"full_metal_body": {
"effect": "full_metal_body"
},
"fur_coat": {
"effect": "fur_coat"
},
"forewarn": {},
"friend_guard": {},
"frisk": {},
"full_metal_body": {},
"fur_coat": {},
"gale_wings": {},
"galvanize": {},
"gluttony": {},

View File

@ -18247,6 +18247,126 @@
],
"formeChange": []
}
},
"sunshine": {
"abilities": [
"flower_gift"
],
"hiddenAbilities": [],
"baseStats": {
"attack": 90,
"defense": 70,
"hp": 70,
"specialAttack": 87,
"specialDefense": 117,
"speed": 85
},
"evReward": {
"specialAttack": 2
},
"types": [
"grass"
],
"height": 0.5,
"weight": 9.3,
"baseExp": 158,
"moves": {
"levelMoves": [
{
"name": "petal_dance",
"level": 0
},
{
"name": "morning_sun",
"level": 1
},
{
"name": "leech_seed",
"level": 1
},
{
"name": "petal_dance",
"level": 1
},
{
"name": "growth",
"level": 1
},
{
"name": "tackle",
"level": 1
},
{
"name": "growth",
"level": 7
},
{
"name": "leech_seed",
"level": 10
},
{
"name": "helping_hand",
"level": 13
},
{
"name": "magical_leaf",
"level": 19
},
{
"name": "sunny_day",
"level": 22
},
{
"name": "worry_seed",
"level": 30
},
{
"name": "take_down",
"level": 35
},
{
"name": "solar_beam",
"level": 43
},
{
"name": "lucky_chant",
"level": 48
},
{
"name": "petal_blizzard",
"level": 50
}
],
"eggMoves": [],
"tutorMoves": [],
"machine": [
"swords_dance",
"hyper_beam",
"solar_beam",
"toxic",
"double_team",
"rest",
"substitute",
"protect",
"swagger",
"attract",
"sleep_talk",
"return",
"frustration",
"safeguard",
"hidden_power",
"sunny_day",
"facade",
"nature_power",
"energy_ball",
"giga_impact",
"grass_knot",
"round",
"confide",
"dazzling_gleam"
],
"formeChange": []
}
}
},
"evolutions": []

View File

@ -19,7 +19,7 @@ public class AngerPoint : Script
{
BatchId = batchId,
});
target.ChangeStatBoost(Statistic.Attack, 12, true, batchId);
target.ChangeStatBoost(Statistic.Attack, 12, true, false, batchId);
}
}
}

View File

@ -18,6 +18,6 @@ public class BeastBoost : Script
{
BatchId = batchId,
});
move.User.ChangeStatBoost(highestStat, 1, true, batchId);
move.User.ChangeStatBoost(highestStat, 1, true, false, batchId);
}
}

View File

@ -22,6 +22,6 @@ public class Berserk : Script
{
BatchId = batchId,
});
pokemon.ChangeStatBoost(Statistic.SpecialAttack, 1, true, batchId);
pokemon.ChangeStatBoost(Statistic.SpecialAttack, 1, true, false, batchId);
}
}

View File

@ -13,7 +13,8 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
public class Comatose : Script
{
/// <inheritdoc />
public override void PreventStatusChange(IPokemon pokemonImpl, StringKey status, ref bool preventStatus)
public override void PreventStatusChange(IPokemon pokemon, StringKey status, bool selfInflicted,
ref bool preventStatus)
{
if (status == ScriptUtils.ResolveName<Status.Sleep>())
{

View File

@ -22,6 +22,6 @@ public class Competitive : Script
{
BatchId = batchId,
});
pokemon.ChangeStatBoost(Statistic.SpecialAttack, 2, true, batchId);
pokemon.ChangeStatBoost(Statistic.SpecialAttack, 2, true, false, batchId);
}
}

View File

@ -22,6 +22,6 @@ public class Defiant : Script
{
BatchId = batchId,
});
pokemon.ChangeStatBoost(Statistic.Attack, 2, true, batchId);
pokemon.ChangeStatBoost(Statistic.Attack, 2, true, false, batchId);
}
}

View File

@ -31,6 +31,6 @@ public class Download : Script
});
pokemon.ChangeStatBoost(
opponentAverageDefense < opponentAverageSpecialDefense ? Statistic.Attack : Statistic.SpecialAttack, 1,
true, batchId);
true, false, batchId);
}
}

View File

@ -36,13 +36,13 @@ public class EffectSpore : Script
switch (chance)
{
case < 9:
move.User.SetStatus(ScriptUtils.ResolveName<Status.Poisoned>(), batchId);
move.User.SetStatus(ScriptUtils.ResolveName<Status.Poisoned>(), false, batchId);
break;
case < 19:
move.User.SetStatus(ScriptUtils.ResolveName<Status.Paralyzed>(), batchId);
move.User.SetStatus(ScriptUtils.ResolveName<Status.Paralyzed>(), false, batchId);
break;
case < 30:
move.User.SetStatus(ScriptUtils.ResolveName<Status.Sleep>(), batchId);
move.User.SetStatus(ScriptUtils.ResolveName<Status.Sleep>(), false, batchId);
break;
}
}

View File

@ -20,6 +20,6 @@ public class FlameBody : Script
return;
move.Battle.EventHook.Invoke(new AbilityTriggerEvent(target));
move.User.SetStatus(ScriptUtils.ResolveName<Status.Burned>());
move.User.SetStatus(ScriptUtils.ResolveName<Status.Burned>(), false);
}
}

View File

@ -16,8 +16,8 @@ public class FlashFire : Script
public override void ChangeIncomingEffectiveness(IExecutingMove executingMove, IPokemon target, byte hitIndex,
ref float effectiveness)
{
if (executingMove.GetHitData(target, hitIndex).Type?.Name == "fire")
{
if (executingMove.GetHitData(target, hitIndex).Type?.Name != "fire")
return;
effectiveness = 0f;
if (target.Volatile.Contains<FlashFireEffect>())
@ -25,5 +25,4 @@ public class FlashFire : Script
target.Volatile.Add(new FlashFireEffect());
executingMove.Battle.EventHook.Invoke(new AbilityTriggerEvent(target));
}
}
}

View File

@ -0,0 +1,60 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Flower Gift is an ability that boosts the Attack and Special Defense of the Pokémon and its allies in sunlight.
/// This ability is exclusive to Cherrim.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Flower_Gift_(Ability)">Bulbapedia - Flower Gift</see>
/// </summary>
[Script(ScriptCategory.Ability, "flower_gift")]
public class FlowerGift : Script
{
private IPokemon? _pokemon;
/// <inheritdoc />
public override void OnAddedToParent(IScriptSource source)
{
if (source is not IPokemon pokemon)
throw new InvalidOperationException("Flower Gift can only be added to a Pokemon script source.");
_pokemon = pokemon;
var effect = _pokemon.BattleData?.BattleSide.VolatileScripts.Add(new Side.FlowerGiftEffect());
(effect?.Script as Side.FlowerGiftEffect)?.OnAdded(_pokemon);
}
/// <inheritdoc />
public override void OnWeatherChange(IBattle battle, StringKey? weatherName, StringKey? oldWeatherName)
{
if (_pokemon is null)
return;
if (weatherName != ScriptUtils.ResolveName<Weather.HarshSunlight>())
return;
if (_pokemon.Species.Name != "cherrim")
return;
EventBatchId batchId = new();
if (_pokemon.Species.TryGetForm("sunshine", out var form) && _pokemon.Form != form)
{
_pokemon.ChangeForm(form, batchId);
}
}
/// <inheritdoc />
public override void OnRemove()
{
if (_pokemon is null)
return;
if (_pokemon.BattleData?.BattleSide.VolatileScripts.TryGet<Side.FlowerGiftEffect>(out var script) == true)
{
script.OnRemoved(_pokemon);
}
if (_pokemon.Species.Name != "cherrim")
return;
EventBatchId batchId = new();
var defaultForm = _pokemon.Species.GetDefaultForm();
if (_pokemon.Form != defaultForm)
{
_pokemon.ChangeForm(defaultForm, batchId);
}
}
}

View File

@ -0,0 +1,34 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Flower Veil is an ability that prevents Grass-type allies from having their stats lowered or being afflicted by status conditions.
/// This ability is exclusive to Florges and its evolutionary line.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Flower_Veil_(Ability)">Bulbapedia - Flower Veil</see>
/// </summary>
[Script(ScriptCategory.Ability, "flower_veil")]
public class FlowerVeil : Script
{
private IPokemon? _pokemon;
/// <inheritdoc />
public override void OnAddedToParent(IScriptSource source)
{
if (source is not IPokemon pokemon)
throw new InvalidOperationException("Flower Veil can only be added to a Pokemon script source.");
_pokemon = pokemon;
var effect = _pokemon.BattleData?.BattleSide.VolatileScripts.Add(new Side.FlowerVeilEffect());
(effect?.Script as Side.FlowerVeilEffect)?.OnAdded(_pokemon);
}
/// <inheritdoc />
public override void OnRemove()
{
if (_pokemon is null)
return;
if (_pokemon.BattleData?.BattleSide.VolatileScripts.TryGet<Side.FlowerVeilEffect>(out var script) == true)
{
script.OnRemoved(_pokemon);
}
}
}

View File

@ -0,0 +1,24 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Fluffy is an ability that halves the damage taken from contact moves but doubles the damage taken from Fire-type moves.
/// This ability is exclusive to Stufful and Bewear.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Fluffy_(Ability)">Bulbapedia - Fluffy</see>
/// </summary>
[Script(ScriptCategory.Ability, "fluffy")]
public class Fluffy : Script
{
/// <inheritdoc />
public override void ChangeDamageModifier(IExecutingMove move, IPokemon target, byte hit, ref float modifier)
{
if (move.GetHitData(target, hit).Type?.Name == "fire")
{
modifier *= 2f;
}
if (move.UseMove.HasFlag("contact"))
{
modifier *= 0.5f;
}
}
}

View File

@ -0,0 +1,71 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Forecast is an ability that changes the Pokémon's form depending on the weather.
/// This ability is exclusive to Castform.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Forecast_(Ability)">Bulbapedia - Forecast</see>
/// </summary>
[Script(ScriptCategory.Ability, "forecast")]
public class Forecast : Script
{
private IPokemon? _pokemon;
/// <inheritdoc />
public override void OnAddedToParent(IScriptSource source)
{
if (source is not IPokemon pokemon)
throw new InvalidOperationException("Forecast can only be added to a Pokemon script source.");
_pokemon = pokemon;
}
/// <inheritdoc />
public override void OnSwitchIn(IPokemon pokemon, byte position)
{
ChangeForm(pokemon, pokemon.BattleData?.Battle.WeatherName);
}
/// <inheritdoc />
public override void OnWeatherChange(IBattle battle, StringKey? weatherName, StringKey? oldWeatherName)
{
if (_pokemon is null)
return;
ChangeForm(_pokemon, weatherName);
}
/// <inheritdoc />
public override void OnRemove()
{
if (_pokemon is null)
return;
ChangeForm(_pokemon, null);
}
private static void ChangeForm(IPokemon pokemon, StringKey? weather)
{
if (pokemon.Species.Name != "castform")
return;
if (weather == ScriptUtils.ResolveName<Weather.HarshSunlight>() &&
pokemon.Species.TryGetForm("sunny", out var sunnyForm) && pokemon.Form != sunnyForm)
{
pokemon.ChangeForm(sunnyForm);
}
else if (weather == ScriptUtils.ResolveName<Weather.Rain>() &&
pokemon.Species.TryGetForm("rainy", out var rainyForm) && pokemon.Form != rainyForm)
{
pokemon.ChangeForm(rainyForm);
}
else if (weather == ScriptUtils.ResolveName<Weather.Hail>() &&
pokemon.Species.TryGetForm("snowy", out var snowyForm) && pokemon.Form != snowyForm)
{
pokemon.ChangeForm(snowyForm);
}
else if (pokemon.Form != pokemon.Species.GetDefaultForm())
{
pokemon.ChangeForm(pokemon.Species.GetDefaultForm());
}
}
}

View File

@ -0,0 +1,64 @@
using PkmnLib.Static.Moves;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Forewarn is an ability that reveals the opponent's move with the highest base power when the Pokémon enters battle.
/// This ability is commonly associated with Drowzee and Hypno.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Forewarn_(Ability)">Bulbapedia - Forewarn</see>
/// </summary>
[Script(ScriptCategory.Ability, "forewarn")]
public class Forewarn : Script
{
/// <inheritdoc />
public override void OnSwitchIn(IPokemon pokemon, byte position)
{
var battleData = pokemon.BattleData;
if (battleData == null)
return;
var opponents = battleData.Battle.Sides.Where(x => x != battleData.BattleSide).SelectMany(x => x.Pokemon)
.WhereNotNull();
var highestPowerMove = opponents.SelectMany(opponent => opponent.Moves).WhereNotNull().Select(move => new
{
Move = move,
Power = GetBasePower(move.MoveData),
}).OrderByDescending(move => move.Power).FirstOrDefault();
battleData.Battle.EventHook.Invoke(new AbilityTriggerEvent(pokemon)
{
Metadata = new Dictionary<StringKey, object?>
{
{ "highest_power_move", highestPowerMove?.Move },
{ "power", highestPowerMove?.Power },
},
});
}
private static byte GetBasePower(IMoveData moveData)
{
// OHKO moves (handled by secondary effect)
if (moveData.SecondaryEffect?.Name == "one_hit_ko")
return 150;
return moveData.Name.ToString() switch
{
// 150 BP: Specific moves
"blast_burn" or "eruption" or "water_spout" or "fissure" or "guillotine" or "horn_drill"
or "sheer_cold" => 150,
// 120 BP: Counter, Metal Burst, Mirror Coat
"counter" or "metal_burst" or "mirror_coat" => 120,
// 80 BP: List of variable power and fixed-damage moves
"crush_grip" or "dragon_rage" or "electro_ball" or "endeavor" or "final_gambit" or "flail" or "fling"
or "frustration" or "grass_knot" or "guardian_of_alola" or "gyro_ball" or "heat_crash" or "heavy_slam"
or "hidden_power" or "low_kick" or "natural_gift" or "natures_madness" or "night_shade" or "present"
or "psywave" or "punishment" or "return" or "reversal" or "seismic_toss" or "sonic_boom" or "spit_up"
or "super_fang" or "trump_card" or "wring_out" => 80,
// 20 BP: Stored Power, Power Trip
"stored_power" or "power_trip" => 20,
_ => moveData.BasePower == 0 ? (byte)1 : moveData.BasePower,
};
}
}

View File

@ -0,0 +1,34 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Friend Guard is an ability that reduces the damage taken by allies by 25%.
/// This ability is commonly associated with Clefairy and Vivillon.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Friend_Guard_(Ability)">Bulbapedia - Friend Guard</see>
/// </summary>
[Script(ScriptCategory.Ability, "friend_guard")]
public class FriendGuard : Script
{
private IPokemon? _pokemon;
/// <inheritdoc />
public override void OnAddedToParent(IScriptSource source)
{
if (source is not IPokemon pokemon)
throw new InvalidOperationException("Friend Guard can only be added to a Pokemon script source.");
_pokemon = pokemon;
var effect = _pokemon.BattleData?.BattleSide.VolatileScripts.Add(new Side.FriendGuardEffect());
(effect?.Script as Side.FriendGuardEffect)?.OnAdded(_pokemon);
}
/// <inheritdoc />
public override void OnRemove()
{
if (_pokemon is null)
return;
if (_pokemon.BattleData?.BattleSide.VolatileScripts.TryGet<Side.FriendGuardEffect>(out var script) == true)
{
script.OnRemoved(_pokemon);
}
}
}

View File

@ -0,0 +1,43 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Frisk is an ability that reveals the held items of opposing Pokémon when the Pokémon enters battle.
/// This ability is commonly associated with Banette and Gothitelle.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Frisk_(Ability)">Bulbapedia - Frisk</see>
/// </summary>
[Script(ScriptCategory.Ability, "frisk")]
public class Frisk : Script
{
/// <inheritdoc />
public override void OnSwitchIn(IPokemon pokemon, byte position)
{
if (pokemon.BattleData?.BattleSide is null)
return;
// Check if the Pokémon has an opposing side
var opposingSide =
pokemon.BattleData.Battle.Sides.FirstOrDefault(side => side != pokemon.BattleData.BattleSide);
if (opposingSide is null)
return;
EventBatchId batchId = new();
// Iterate through the opposing Pokémon
foreach (var opponent in opposingSide.Pokemon.WhereNotNull())
{
// If the opponent has a held item, reveal it
if (opponent.HeldItem != null)
{
pokemon.BattleData.Battle.EventHook.Invoke(new AbilityTriggerEvent(pokemon)
{
Metadata = new Dictionary<StringKey, object?>
{
{ "opponent", opponent },
{ "held_item", opponent.HeldItem },
},
BatchId = batchId,
});
}
}
}
}

View File

@ -0,0 +1,21 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Full Metal Body is an ability that prevents the Pokémon's stats from being lowered by other Pokémon's moves or abilities.
/// This ability is exclusive to Solgaleo.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Full_Metal_Body_(Ability)">Bulbapedia - Full Metal Body</see>
/// </summary>
[Script(ScriptCategory.Ability, "full_metal_body")]
public class FullMetalBody : Script
{
/// <inheritdoc />
public override void PreventStatBoostChange(IPokemon target, Statistic stat, sbyte amount, bool selfInflicted,
ref bool prevent)
{
if (selfInflicted)
return;
prevent = true;
}
}

View File

@ -0,0 +1,22 @@
using PkmnLib.Static.Moves;
namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
/// <summary>
/// Fur Coat is an ability that halves the damage taken from physical moves.
/// This ability is exclusive to Furfrou.
///
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Fur_Coat_(Ability)">Bulbapedia - Fur Coat</see>
/// </summary>
[Script(ScriptCategory.Ability, "fur_coat")]
public class FurCoat : Script
{
/// <inheritdoc />
public override void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{
if (move.UseMove.Category == MoveCategory.Physical)
{
damage /= 2;
}
}
}

View File

@ -16,7 +16,8 @@ public class UproarEffect : Script
}
/// <inheritdoc />
public override void PreventStatusChange(IPokemon pokemonImpl, StringKey status, ref bool preventStatus)
public override void PreventStatusChange(IPokemon pokemon, StringKey status, bool selfInflicted,
ref bool preventStatus)
{
if (status == ScriptUtils.ResolveName<Status.Sleep>())
{

View File

@ -23,6 +23,6 @@ public class Acupressure : Script
// Choose a random stat to raise. 0 is HP, so we start at 1.
var stat = (Statistic)move.User.BattleData!.Battle.Random.GetInt(1, (int)Statistic.Speed + 1);
target.ChangeStatBoost(stat, 2, false);
target.ChangeStatBoost(stat, 2, false, false);
}
}

View File

@ -21,7 +21,7 @@ public class Autotomize : Script
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var user = move.User;
if (user.ChangeStatBoost(Statistic.Speed, 2, true) && user.ChangeWeightInKgBy(-100.0f))
if (user.ChangeStatBoost(Statistic.Speed, 2, true, false) && user.ChangeWeightInKgBy(-100.0f))
{
var battle = user.BattleData?.Battle;
battle?.EventHook.Invoke(new DialogEvent("pokemon_became_nimble", new Dictionary<string, object>

View File

@ -15,6 +15,6 @@ public class BellyDrum : Script
target.Damage(maxHealthHalved, DamageSource.Misc);
// Raising the user's Attack by 12 stages should always set it to +6.
target.ChangeStatBoost(Statistic.Attack, 12, true);
target.ChangeStatBoost(Statistic.Attack, 12, true, false);
}
}

View File

@ -34,7 +34,7 @@ public class Bounce : Script
var random = battle.Random;
if (random.EffectChance(30, move, target, hit))
{
target.SetStatus("paralyzed");
target.SetStatus("paralyzed", false);
}
}
}

View File

@ -19,6 +19,6 @@ public class Captivate : Script
move.GetHitData(target, hit).Fail();
return;
}
target.ChangeStatBoost(Statistic.SpecialAttack, -2, false);
target.ChangeStatBoost(Statistic.SpecialAttack, -2, false, false);
}
}

View File

@ -24,10 +24,10 @@ public class ChangeAllTargetStats : Script
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
target.ChangeStatBoost(Statistic.Attack, _amount, target == move.User);
target.ChangeStatBoost(Statistic.Defense, _amount, target == move.User);
target.ChangeStatBoost(Statistic.SpecialAttack, _amount, target == move.User);
target.ChangeStatBoost(Statistic.SpecialDefense, _amount, target == move.User);
target.ChangeStatBoost(Statistic.Speed, _amount, target == move.User);
target.ChangeStatBoost(Statistic.Attack, _amount, target == move.User, false);
target.ChangeStatBoost(Statistic.Defense, _amount, target == move.User, false);
target.ChangeStatBoost(Statistic.SpecialAttack, _amount, target == move.User, false);
target.ChangeStatBoost(Statistic.SpecialDefense, _amount, target == move.User, false);
target.ChangeStatBoost(Statistic.Speed, _amount, target == move.User, false);
}
}

View File

@ -32,7 +32,7 @@ public class ChangeMultipleTargetStatBoosts : Script
EventBatchId batchId = new();
foreach (var stat in _statBoosts)
{
target.ChangeStatBoost(stat.Key, stat.Value, true, batchId);
target.ChangeStatBoost(stat.Key, stat.Value, true, false, batchId);
}
}
}

View File

@ -32,7 +32,7 @@ public class ChangeMultipleUserStatBoosts : Script
EventBatchId batchId = new();
foreach (var stat in _statBoosts)
{
move.User.ChangeStatBoost(stat.Key, stat.Value, true, batchId);
move.User.ChangeStatBoost(stat.Key, stat.Value, true, false, batchId);
}
}
}

View File

@ -31,7 +31,7 @@ public abstract class ChangeTargetStats : Script
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
target.ChangeStatBoost(_stat, _amount, target == move.User);
target.ChangeStatBoost(_stat, _amount, target == move.User, false);
}
}

View File

@ -31,7 +31,7 @@ public abstract class ChangeUserStats : Script
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
move.User.ChangeStatBoost(_stat, _amount, true);
move.User.ChangeStatBoost(_stat, _amount, true, false);
}
}

View File

@ -8,7 +8,7 @@ public class Charge : Script
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
move.User.ChangeStatBoost(Statistic.SpecialDefense, 1, true);
move.User.ChangeStatBoost(Statistic.SpecialDefense, 1, true, false);
move.User.Volatile.Add(new ChargeEffect());
}
}

View File

@ -24,9 +24,9 @@ public class Curse : Script
else
{
EventBatchId batchId = new();
move.User.ChangeStatBoost(Statistic.Speed, -1, true, batchId);
move.User.ChangeStatBoost(Statistic.Defense, 1, true, batchId);
move.User.ChangeStatBoost(Statistic.Attack, 1, true, batchId);
move.User.ChangeStatBoost(Statistic.Speed, -1, true, false, batchId);
move.User.ChangeStatBoost(Statistic.Defense, 1, true, false, batchId);
move.User.ChangeStatBoost(Statistic.Attack, 1, true, false, batchId);
}
}
}

View File

@ -7,7 +7,7 @@ public class DragonAscent : Script
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
EventBatchId batchId = new();
move.User.ChangeStatBoost(Statistic.Defense, -1, true, batchId);
move.User.ChangeStatBoost(Statistic.SpecialDefense, -1, true, batchId);
move.User.ChangeStatBoost(Statistic.Defense, -1, true, false, batchId);
move.User.ChangeStatBoost(Statistic.SpecialDefense, -1, true, false, batchId);
}
}

View File

@ -8,7 +8,7 @@ public class FellStinger : Script
{
if (target.IsFainted)
{
move.User.ChangeStatBoost(Statistic.Attack, 2, true);
move.User.ChangeStatBoost(Statistic.Attack, 2, true, false);
}
}
}

View File

@ -14,7 +14,7 @@ public class FireFang : Script
var random = battleData.Battle.Random;
if (random.EffectChance(10, move, target, hit))
{
target.SetStatus("burned");
target.SetStatus("burned", false);
}
// It also has an independent 10% chance of causing the target to flinch, if the user attacks before the target.

View File

@ -28,7 +28,7 @@ public class FlameWheel : Script
if (move.Battle.Random.EffectChance(_burnChance, move, target, hit))
{
target.SetStatus("burned");
target.SetStatus("burned", false);
}
}
}

View File

@ -12,7 +12,7 @@ public class FlareBlitz : Script
if (battleData.Battle.Random.EffectChance(10, move, target, hit))
{
target.SetStatus("burned");
target.SetStatus("burned", false);
}
var hitData = move.GetHitData(target, hit);

View File

@ -8,7 +8,7 @@ public class Flatter : Script
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
target.ChangeStatBoost(Statistic.SpecialAttack, 1, false);
target.ChangeStatBoost(Statistic.SpecialAttack, 1, false, false);
target.Volatile.StackOrAdd("confusion", () => new Confusion());
}
}

View File

@ -22,7 +22,7 @@ public class FlowerShield : Script
continue;
if (!pokemon.Types.Contains(grassType))
continue;
pokemon.ChangeStatBoost(Statistic.Defense, 1, pokemon == move.User, batchId);
pokemon.ChangeStatBoost(Statistic.Defense, 1, pokemon == move.User, false, batchId);
}
}
}

View File

@ -13,6 +13,6 @@ public class Foresight : Script
return;
var typeLibrary = battleData.Battle.Library.StaticLibrary.Types;
target.Volatile.Add(new ForesightEffect(typeLibrary));
target.ChangeStatBoost(Statistic.Evasion, (sbyte)-target.StatBoost.Evasion, false);
target.ChangeStatBoost(Statistic.Evasion, (sbyte)-target.StatBoost.Evasion, false, false);
}
}

View File

@ -31,7 +31,7 @@ public class FreezeDry : Script
if (battleData.Battle.Random.EffectChance(10, move, target, hit))
{
target.SetStatus("frozen");
target.SetStatus("frozen", false);
}
}
}

View File

@ -17,7 +17,7 @@ public class FreezeShock : BaseChargeMove<RequireChargeEffect>
if (battleData.Battle.Random.EffectChance(30, move, target, hit))
{
target.SetStatus("paralyzed");
target.SetStatus("paralyzed", false);
}
}
}

View File

@ -19,8 +19,8 @@ public class GearUp : Script
var ability = pokemon.ActiveAbility?.Name;
if (ability != "plus" && ability != "minus")
continue;
pokemon.ChangeStatBoost(Statistic.Attack, 1, pokemon == move.User, evtBatchId);
pokemon.ChangeStatBoost(Statistic.SpecialAttack, 1, pokemon == move.User, evtBatchId);
pokemon.ChangeStatBoost(Statistic.Attack, 1, pokemon == move.User, false, evtBatchId);
pokemon.ChangeStatBoost(Statistic.SpecialAttack, 1, pokemon == move.User, false, evtBatchId);
}
}
}

View File

@ -12,8 +12,8 @@ public class Geomancy : BaseChargeMove<RequireChargeEffect>
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
EventBatchId eventBatchId = new();
move.User.ChangeStatBoost(Statistic.SpecialAttack, 2, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.SpecialDefense, 2, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.Speed, 2, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.SpecialAttack, 2, true, false, eventBatchId);
move.User.ChangeStatBoost(Statistic.SpecialDefense, 2, true, false, eventBatchId);
move.User.ChangeStatBoost(Statistic.Speed, 2, true, false, eventBatchId);
}
}

View File

@ -12,7 +12,7 @@ public class Growth : Script
sbyte amount = 1;
if (move.Battle.WeatherName == ScriptUtils.ResolveName<HarshSunlight>())
amount = 2;
move.User.ChangeStatBoost(Statistic.Attack, amount, true, batchId);
move.User.ChangeStatBoost(Statistic.SpecialAttack, amount, true, batchId);
move.User.ChangeStatBoost(Statistic.Attack, amount, true, false, batchId);
move.User.ChangeStatBoost(Statistic.SpecialAttack, amount, true, false, batchId);
}
}

View File

@ -17,11 +17,11 @@ public class GuardSwap : Script
var userSpecialDefense = userStats.GetStatistic(Statistic.SpecialDefense);
var targetSpecialDefense = targetStats.GetStatistic(Statistic.SpecialDefense);
user.ChangeStatBoost(Statistic.Defense, (sbyte)(targetDefense - userDefense), true, eventBatchId);
user.ChangeStatBoost(Statistic.SpecialDefense, (sbyte)(targetSpecialDefense - userSpecialDefense), true,
user.ChangeStatBoost(Statistic.Defense, (sbyte)(targetDefense - userDefense), true, true, eventBatchId);
user.ChangeStatBoost(Statistic.SpecialDefense, (sbyte)(targetSpecialDefense - userSpecialDefense), true, true,
eventBatchId);
target.ChangeStatBoost(Statistic.Defense, (sbyte)(userDefense - targetDefense), false, eventBatchId);
target.ChangeStatBoost(Statistic.Defense, (sbyte)(userDefense - targetDefense), false, true, eventBatchId);
target.ChangeStatBoost(Statistic.SpecialDefense, (sbyte)(userSpecialDefense - targetSpecialDefense), false,
eventBatchId);
true, eventBatchId);
}
}

View File

@ -16,8 +16,8 @@ public class HeartSwap : Script
var targetStat = targetStats.GetStatistic(stat);
if (userStat == targetStat)
continue;
move.User.ChangeStatBoost(stat, (sbyte)(userStat - targetStat), true, eventBatchId);
target.ChangeStatBoost(stat, (sbyte)(targetStat - userStat), false, eventBatchId);
move.User.ChangeStatBoost(stat, (sbyte)(userStat - targetStat), true, true, eventBatchId);
target.ChangeStatBoost(stat, (sbyte)(targetStat - userStat), false, true, eventBatchId);
}
}
}

View File

@ -17,7 +17,7 @@ public class IceBurn : BaseChargeMove<RequireChargeEffect>
if (battleData.Battle.Random.EffectChance(30, move, target, hit))
{
target.SetStatus("burned");
target.SetStatus("burned", false);
}
}
}

View File

@ -14,7 +14,7 @@ public class IceFang : Script
if (battleData.Battle.Random.EffectChance(10, move, target, hit))
{
target.SetStatus("frozen");
target.SetStatus("frozen", false);
}
if (battleData.Battle.Random.EffectChance(10, move, target, hit))
{

View File

@ -18,8 +18,8 @@ public class MagneticFlux : Script
continue;
EventBatchId batch = new();
pokemon.ChangeStatBoost(Statistic.Defense, 1, pokemon == move.User, batch);
pokemon.ChangeStatBoost(Statistic.SpecialDefense, 1, pokemon == move.User, batch);
pokemon.ChangeStatBoost(Statistic.Defense, 1, pokemon == move.User, false, batch);
pokemon.ChangeStatBoost(Statistic.SpecialDefense, 1, pokemon == move.User, false, batch);
}
}
}

View File

@ -7,8 +7,8 @@ public class Memento : Script
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var evtBatch = new EventBatchId();
target.ChangeStatBoost(Statistic.Attack, -2, false, evtBatch);
target.ChangeStatBoost(Statistic.SpecialAttack, -2, false, evtBatch);
target.ChangeStatBoost(Statistic.Attack, -2, false, false, evtBatch);
target.ChangeStatBoost(Statistic.SpecialAttack, -2, false, false, evtBatch);
move.User.Faint(DamageSource.Misc);
}

View File

@ -10,7 +10,7 @@ public class MiracleEye : Script
{
if (target.StatBoost.Evasion > 0)
{
target.ChangeStatBoost(Statistic.Evasion, (sbyte)-target.StatBoost.Evasion, false);
target.ChangeStatBoost(Statistic.Evasion, (sbyte)-target.StatBoost.Evasion, false, false);
}
target.Volatile.Add(new MiracleEyeEffect());
}

View File

@ -15,8 +15,8 @@ public class PartingShot : Script
return;
var evtBatch = new EventBatchId();
var attackChanged = target.ChangeStatBoost(Statistic.Attack, -1, false, evtBatch);
var specialAttackChanged = target.ChangeStatBoost(Statistic.SpecialAttack, -1, false, evtBatch);
var attackChanged = target.ChangeStatBoost(Statistic.Attack, -1, false, false, evtBatch);
var specialAttackChanged = target.ChangeStatBoost(Statistic.SpecialAttack, -1, false, false, evtBatch);
if (attackChanged || specialAttackChanged)
{

View File

@ -22,7 +22,7 @@ public class PoisonTail : Script
if (battleData.Battle.Random.EffectChance(10, move, target, hit))
{
target.SetStatus(ScriptUtils.ResolveName<BadlyPoisoned>());
target.SetStatus(ScriptUtils.ResolveName<BadlyPoisoned>(), false);
}
}
}

View File

@ -14,11 +14,11 @@ public class PowerSwap : Script
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,
user.ChangeStatBoost(Statistic.Attack, (sbyte)(targetAttack - userAttack), true, true, eventBatchId);
user.ChangeStatBoost(Statistic.SpecialAttack, (sbyte)(targetSpecialAttack - userSpecialAttack), true, true,
eventBatchId);
target.ChangeStatBoost(Statistic.Attack, (sbyte)(userAttack - targetAttack), true, eventBatchId);
target.ChangeStatBoost(Statistic.SpecialAttack, (sbyte)(userSpecialAttack - targetSpecialAttack), true,
target.ChangeStatBoost(Statistic.Attack, (sbyte)(userAttack - targetAttack), true, true, eventBatchId);
target.ChangeStatBoost(Statistic.SpecialAttack, (sbyte)(userSpecialAttack - targetSpecialAttack), true, true,
eventBatchId);
}
}

View File

@ -9,7 +9,7 @@ public class ResetTargetStats : Script
EventBatchId eventBatchId = new();
foreach (Statistic stat in Enum.GetValues(typeof(Statistic)))
{
target.ChangeStatBoost(stat, (sbyte)-target.StatBoost.GetStatistic(stat), true, eventBatchId);
target.ChangeStatBoost(stat, (sbyte)-target.StatBoost.GetStatistic(stat), true, true, eventBatchId);
}
}
}

View File

@ -18,7 +18,7 @@ public class Rest : Script
move.GetHitData(target, hit).Fail();
return;
}
if (move.User.SetStatus(ScriptUtils.ResolveName<Sleep>()) && move.User.StatusScript.Script is Sleep sleep)
if (move.User.SetStatus(ScriptUtils.ResolveName<Sleep>(), true) && move.User.StatusScript.Script is Sleep sleep)
sleep.Turns = 2;
}
}

View File

@ -13,8 +13,8 @@ public class Rototiller : Script
EventBatchId batchId = new();
foreach (var pkmn in pokemon)
{
pkmn.ChangeStatBoost(Statistic.Attack, 1, pkmn == move.User, batchId);
pkmn.ChangeStatBoost(Statistic.SpecialAttack, 1, pkmn == move.User, batchId);
pkmn.ChangeStatBoost(Statistic.Attack, 1, pkmn == move.User, false, batchId);
pkmn.ChangeStatBoost(Statistic.SpecialAttack, 1, pkmn == move.User, false, batchId);
}
}
}

View File

@ -20,6 +20,6 @@ public class SetStatus : Script
{
if (_status == null)
throw new Exception("Missing required parameter 'status'");
target.SetStatus(_status);
target.SetStatus(_status, false);
}
}

View File

@ -7,11 +7,11 @@ public class ShellSmash : Script
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
EventBatchId eventBatchId = new();
move.User.ChangeStatBoost(Statistic.Attack, 2, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.SpecialAttack, 2, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.Speed, 2, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.Attack, 2, true, false, eventBatchId);
move.User.ChangeStatBoost(Statistic.SpecialAttack, 2, true, false, eventBatchId);
move.User.ChangeStatBoost(Statistic.Speed, 2, true, false, eventBatchId);
eventBatchId = new EventBatchId();
move.User.ChangeStatBoost(Statistic.Defense, -1, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.SpecialDefense, -1, true, eventBatchId);
move.User.ChangeStatBoost(Statistic.Defense, -1, true, false, eventBatchId);
move.User.ChangeStatBoost(Statistic.SpecialDefense, -1, true, false, eventBatchId);
}
}

View File

@ -10,7 +10,7 @@ public class SkullBash : Script
{
if (move.User.Volatile.Contains<SkullBashEffect>())
return;
move.User.ChangeStatBoost(Statistic.Defense, 1, true);
move.User.ChangeStatBoost(Statistic.Defense, 1, true, false);
move.User.Volatile.Add(new SkullBashEffect(move.User));
prevent = true;
}

View File

@ -12,8 +12,8 @@ public class SpectralThief : Script
EventBatchId batchId = new();
foreach (var positiveStat in positiveStats)
{
move.User.ChangeStatBoost(positiveStat.statistic, positiveStat.value, true, batchId);
target.ChangeStatBoost(positiveStat.statistic, (sbyte)-positiveStat.value, true, batchId);
move.User.ChangeStatBoost(positiveStat.statistic, positiveStat.value, true, true, batchId);
target.ChangeStatBoost(positiveStat.statistic, (sbyte)-positiveStat.value, true, true, batchId);
}
}
}

View File

@ -7,7 +7,7 @@ public class StrengthSap : Script
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
var attack = target.BoostedStats.Attack;
if (!target.ChangeStatBoost(Statistic.Attack, -1, false))
if (!target.ChangeStatBoost(Statistic.Attack, -1, false, false))
{
move.GetHitData(target, hit).Fail();
return;

View File

@ -7,7 +7,7 @@ public class Superpower : Script
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
EventBatchId eventBatch = new();
move.User.ChangeStatBoost(Statistic.Attack, -1, true, eventBatch);
move.User.ChangeStatBoost(Statistic.Defense, -1, true, eventBatch);
move.User.ChangeStatBoost(Statistic.Attack, -1, true, false, eventBatch);
move.User.ChangeStatBoost(Statistic.Defense, -1, true, false, eventBatch);
}
}

View File

@ -6,7 +6,7 @@ public class Swagger : Script
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
target.ChangeStatBoost(Statistic.Attack, 2, false);
target.ChangeStatBoost(Statistic.Attack, 2, false, false);
target.Volatile.Add(new Pokemon.Confusion());
}
}

View File

@ -12,7 +12,7 @@ public class ThunderFang : Script
var random = move.Battle.Random;
if (random.EffectChance(10, move, target, hit))
{
target.SetStatus(ScriptUtils.ResolveName<Paralyzed>());
target.SetStatus(ScriptUtils.ResolveName<Paralyzed>(), false);
}
if (random.EffectChance(10, move, target, hit))
{

View File

@ -16,7 +16,7 @@ public class TopsyTurvy : Script
hasChanged = true;
var newStatBoost = -statBoost;
target.ChangeStatBoost(stat, (sbyte)newStatBoost, target == move.User, batchId);
target.ChangeStatBoost(stat, (sbyte)newStatBoost, target == move.User, true, batchId);
}
if (!hasChanged)
{

View File

@ -6,7 +6,7 @@ public class ToxicThread : Script
/// <inheritdoc />
public override void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
{
target.SetStatus(ScriptUtils.ResolveName<Status.Poisoned>());
target.ChangeStatBoost(Statistic.Speed, -1, false);
target.SetStatus(ScriptUtils.ResolveName<Status.Poisoned>(), false);
target.ChangeStatBoost(Statistic.Speed, -1, false, false);
}
}

View File

@ -14,6 +14,6 @@ public class TriAttack : Script
ScriptUtils.ResolveName<Status.Paralyzed>(),
ScriptUtils.ResolveName<Status.Frozen>(),
]);
target.SetStatus(status);
target.SetStatus(status, false);
}
}

View File

@ -11,7 +11,7 @@ public class Twineedle : Script
{
if (move.Battle.Random.EffectChance(20, move, target, hit))
{
target.SetStatus(ScriptUtils.ResolveName<Status.Poisoned>());
target.SetStatus(ScriptUtils.ResolveName<Status.Poisoned>(), false);
}
}
}

View File

@ -11,8 +11,8 @@ public class VenomDrench : Script
return;
EventBatchId eventBatch = new();
target.ChangeStatBoost(Statistic.Attack, -1, false, eventBatch);
target.ChangeStatBoost(Statistic.SpecialAttack, -1, false, eventBatch);
target.ChangeStatBoost(Statistic.Speed, -1, false, eventBatch);
target.ChangeStatBoost(Statistic.Attack, -1, false, false, eventBatch);
target.ChangeStatBoost(Statistic.SpecialAttack, -1, false, false, eventBatch);
target.ChangeStatBoost(Statistic.Speed, -1, false, false, eventBatch);
}
}

View File

@ -12,7 +12,7 @@ public class VoltTackle : Script
if (move.Battle.Random.EffectChance(10, move, target, hit))
{
target.SetStatus(ScriptUtils.ResolveName<Status.Paralyzed>());
target.SetStatus(ScriptUtils.ResolveName<Status.Paralyzed>(), false);
}
}
}

View File

@ -11,7 +11,7 @@ public class BanefulBunkerEffect : ProtectionEffectScript
base.BlockIncomingHit(executingMove, target, hitIndex, ref block);
if (executingMove.UseMove.Category != MoveCategory.Status && executingMove.UseMove.HasFlag("contact"))
{
executingMove.User.SetStatus("poisoned");
executingMove.User.SetStatus("poisoned", false);
}
}
}

View File

@ -8,7 +8,7 @@ public class BeakBlastEffect : Script
{
if (move.UseMove.HasFlag("contact"))
{
move.User.SetStatus("burned");
move.User.SetStatus("burned", false);
}
}
}

View File

@ -11,7 +11,7 @@ public class KingsShield : ProtectionEffectScript
base.BlockIncomingHit(executingMove, target, hitIndex, ref block);
if (executingMove.UseMove.Category != MoveCategory.Status && executingMove.UseMove.HasFlag("contact"))
{
executingMove.User.ChangeStatBoost(Statistic.Accuracy, -2, false);
executingMove.User.ChangeStatBoost(Statistic.Accuracy, -2, false, false);
}
}
}

View File

@ -6,7 +6,7 @@ public class RageEffect : Script
/// <inheritdoc />
public override void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit)
{
move.User.ChangeStatBoost(Statistic.Attack, 1, true);
move.User.ChangeStatBoost(Statistic.Attack, 1, true, false);
}
/// <inheritdoc />

View File

@ -15,8 +15,8 @@ public class StockpileEffect : Script
}
_pokemon = pokemon;
EventBatchId batchId = new();
pokemon.ChangeStatBoost(Statistic.Defense, 1, true, batchId);
pokemon.ChangeStatBoost(Statistic.SpecialDefense, 1, true, batchId);
pokemon.ChangeStatBoost(Statistic.Defense, 1, true, false, batchId);
pokemon.ChangeStatBoost(Statistic.SpecialDefense, 1, true, false, batchId);
StockpileCount = 1;
}
@ -26,8 +26,8 @@ public class StockpileEffect : Script
if (StockpileCount < 3)
{
EventBatchId batchId = new();
_pokemon?.ChangeStatBoost(Statistic.Defense, 1, true, batchId);
_pokemon?.ChangeStatBoost(Statistic.SpecialDefense, 1, true, batchId);
_pokemon?.ChangeStatBoost(Statistic.Defense, 1, true, false, batchId);
_pokemon?.ChangeStatBoost(Statistic.SpecialDefense, 1, true, false, batchId);
StockpileCount++;
}
}
@ -40,8 +40,8 @@ public class StockpileEffect : Script
return;
}
EventBatchId batchId = new();
_pokemon.ChangeStatBoost(Statistic.Defense, (sbyte)-StockpileCount, true, batchId);
_pokemon.ChangeStatBoost(Statistic.SpecialDefense, (sbyte)-StockpileCount, true, batchId);
_pokemon.ChangeStatBoost(Statistic.Defense, (sbyte)-StockpileCount, true, false, batchId);
_pokemon.ChangeStatBoost(Statistic.SpecialDefense, (sbyte)-StockpileCount, true, false, batchId);
StockpileCount = 0;
}
}

View File

@ -31,6 +31,6 @@ public class YawnEffect : Script
_hasDoneFirstTurn = true;
return;
}
_pokemon.SetStatus(ScriptUtils.ResolveName<Status.Sleep>());
_pokemon.SetStatus(ScriptUtils.ResolveName<Status.Sleep>(), true);
}
}

View File

@ -0,0 +1,31 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
[Script(ScriptCategory.Side, "flower_gift")]
public class FlowerGiftEffect : Script
{
private readonly HashSet<IPokemon> _placerPokemon = [];
public void OnAdded(IPokemon placer)
{
_placerPokemon.Add(placer);
}
public void OnRemoved(IPokemon placer)
{
_placerPokemon.Remove(placer);
if (_placerPokemon.Count == 0)
{
RemoveSelf();
}
}
/// <inheritdoc />
public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
ImmutableStatisticSet<uint> targetStats, ref uint value)
{
if (move.Battle.WeatherName != ScriptUtils.ResolveName<Weather.HarshSunlight>())
return;
value = value.MultiplyOrMax(1.5f);
}
}

View File

@ -0,0 +1,49 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
[Script(ScriptCategory.Side, "flower_veil")]
public class FlowerVeilEffect : Script
{
private readonly HashSet<IPokemon> _placerPokemon = [];
public void OnAdded(IPokemon placer)
{
_placerPokemon.Add(placer);
}
public void OnRemoved(IPokemon placer)
{
_placerPokemon.Remove(placer);
if (_placerPokemon.Count == 0)
{
RemoveSelf();
}
}
/// <inheritdoc />
public override void PreventStatBoostChange(IPokemon target, Statistic stat, sbyte amount, bool selfInflicted,
ref bool prevent)
{
if (selfInflicted)
return;
if (amount > 0)
return;
if (target.Types.All(x => x.Name != "grass"))
return;
prevent = true;
}
/// <inheritdoc />
public override void PreventStatusChange(IPokemon pokemon, StringKey status, bool selfInflicted,
ref bool preventStatus)
{
if (selfInflicted)
return;
if (pokemon.Types.All(x => x.Name != "grass"))
return;
preventStatus = true;
}
}

View File

@ -0,0 +1,28 @@
namespace PkmnLib.Plugin.Gen7.Scripts.Side;
[Script(ScriptCategory.Side, "friend_guard")]
public class FriendGuardEffect : Script
{
private readonly HashSet<IPokemon> _placerPokemon = [];
public void OnAdded(IPokemon placer)
{
_placerPokemon.Add(placer);
}
public void OnRemoved(IPokemon placer)
{
_placerPokemon.Remove(placer);
if (_placerPokemon.Count == 0)
{
RemoveSelf();
}
}
/// <inheritdoc />
public override void ChangeIncomingMoveDamage(IExecutingMove move, IPokemon target, byte hit, ref uint damage)
{
var modifier = Math.Pow(0.75f, _placerPokemon.Count);
damage = (uint)(damage * modifier);
}
}

View File

@ -6,7 +6,8 @@ namespace PkmnLib.Plugin.Gen7.Scripts.Side;
public class SafeguardEffect : Script
{
/// <inheritdoc />
public override void PreventStatusChange(IPokemon pokemonImpl, StringKey status, ref bool preventStatus)
public override void PreventStatusChange(IPokemon pokemon, StringKey status, bool selfInflicted,
ref bool preventStatus)
{
preventStatus = true;
}

View File

@ -8,6 +8,6 @@ public class StickyWebEffect : Script
{
if (pokemon.IsFloating)
return;
pokemon.ChangeStatBoost(Statistic.Speed, -1, false);
pokemon.ChangeStatBoost(Statistic.Speed, -1, false, false);
}
}

View File

@ -9,6 +9,6 @@ public class ToxicSpikesEffect : Script
if (pokemon.IsFloating)
return;
pokemon.SetStatus(ScriptUtils.ResolveName<Status.Poisoned>());
pokemon.SetStatus(ScriptUtils.ResolveName<Status.Poisoned>(), false);
}
}

View File

@ -39,7 +39,8 @@ public class HarshSunlight : Script
}
/// <inheritdoc />
public override void PreventStatusChange(IPokemon pokemonImpl, StringKey status, ref bool preventStatus)
public override void PreventStatusChange(IPokemon pokemon, StringKey status, bool selfInflicted,
ref bool preventStatus)
{
if (status == ScriptUtils.ResolveName<Status.Frozen>())
{