diff --git a/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs b/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs
index f95f92a..cd73eee 100644
--- a/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs
+++ b/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs
@@ -76,7 +76,8 @@ public static class MoveTurnExecutor
return;
byte ppUsed = 1;
- // TODO: Modify the PP used by the move.
+ executingMove.RunScriptHook(x => x.ModifyPPUsed(executingMove, ref ppUsed));
+ targets.WhereNotNull().RunScriptHook(x => x.ModifyPPUsedForIncomingMove(executingMove, ref ppUsed));
if (!executingMove.ChosenMove.TryUse(ppUsed))
return;
diff --git a/PkmnLib.Dynamic/Models/Battle.cs b/PkmnLib.Dynamic/Models/Battle.cs
index b4bdf8d..311d48a 100644
--- a/PkmnLib.Dynamic/Models/Battle.cs
+++ b/PkmnLib.Dynamic/Models/Battle.cs
@@ -311,7 +311,6 @@ public class BattleImpl : ScriptSource, IBattle
if (choice is IMoveChoice moveChoice)
{
- // TODO: Hook to change number of PP needed.
if (moveChoice.ChosenMove.CurrentPp < 1)
return false;
if (!TargetResolver.IsValidTarget(moveChoice.TargetSide, moveChoice.TargetPosition,
diff --git a/PkmnLib.Dynamic/Models/Pokemon.cs b/PkmnLib.Dynamic/Models/Pokemon.cs
index 37b3a9d..60d9186 100644
--- a/PkmnLib.Dynamic/Models/Pokemon.cs
+++ b/PkmnLib.Dynamic/Models/Pokemon.cs
@@ -1072,6 +1072,11 @@ public class PokemonImpl : ScriptSource, IPokemon
// Allow scripts to trigger based on the faint.
this.RunScriptHook(script => script.OnFaint(this, source));
+ foreach (var ally in BattleData.BattleSide.Pokemon.WhereNotNull().Where(x => x != this))
+ {
+ ally.RunScriptHook(script => script.OnAllyFaint(ally, this));
+ }
+
// Make sure the OnRemove script is run.
this.RunScriptHook(script => script.OnRemove());
diff --git a/PkmnLib.Dynamic/ScriptHandling/Script.cs b/PkmnLib.Dynamic/ScriptHandling/Script.cs
index ef403c3..14dcdab 100644
--- a/PkmnLib.Dynamic/ScriptHandling/Script.cs
+++ b/PkmnLib.Dynamic/ScriptHandling/Script.cs
@@ -562,6 +562,13 @@ public abstract class Script : IDeepCloneable
{
}
+ ///
+ /// This function is triggered on a Pokemon when an ally Pokemon faints.
+ ///
+ public virtual void OnAllyFaint(IPokemon ally, IPokemon faintedPokemon)
+ {
+ }
+
///
/// This function is triggered on a Pokemon and its parents when the given Pokemon switches out
/// of the battlefield.
@@ -801,4 +808,18 @@ public abstract class Script : IDeepCloneable
public virtual void OnAfterHeldItemChange(IPokemon pokemon, IItem? previous, IItem? item)
{
}
+
+ ///
+ /// This function allows a script to modify the PP used by a move.
+ ///
+ public virtual void ModifyPPUsed(IExecutingMove executingMove, ref byte ppUsed)
+ {
+ }
+
+ ///
+ /// This function allows a script to modify the PP used by an incoming move. This is used for abilities such as Pressure.
+ ///
+ public virtual void ModifyPPUsedForIncomingMove(IExecutingMove executingMove, ref byte ppUsed)
+ {
+ }
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc b/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc
index 227de6a..c229176 100755
--- a/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc
+++ b/Plugins/PkmnLib.Plugin.Gen7/Data/Abilities.jsonc
@@ -424,23 +424,30 @@
"pixilate": {
"effect": "pixilate"
},
- "plus": {},
- "poison_heal": {},
- "poison_point": {},
- "poison_touch": {},
+ "plus": {
+ "effect": "plus"
+ },
+ "poison_heal": {
+ "effect": "poison_heal"
+ },
+ "poison_point": {
+ "effect": "poison_point"
+ },
+ "poison_touch": {
+ "effect": "poison_touch"
+ },
"power_construct": {
- "canBeChanged": false,
- "flags": [
- "cant_be_copied"
- ]
+ "effect": "power_construct"
},
"power_of_alchemy": {
- "flags": [
- "cant_be_copied"
- ]
+ "effect": "power_of_alchemy"
+ },
+ "prankster": {
+ "effect": "prankster"
+ },
+ "pressure": {
+ "effect": "pressure"
},
- "prankster": {},
- "pressure": {},
"primordial_sea": {},
"prism_armor": {},
"protean": {},
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json b/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json
index 0f89d5c..12e7e4f 100755
--- a/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json
+++ b/Plugins/PkmnLib.Plugin.Gen7/Data/Pokemon.json
@@ -145666,6 +145666,7 @@
}
},
"complete": {
+ "isBattleOnly": true,
"abilities": [
"power_construct"
],
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Plus.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Plus.cs
new file mode 100644
index 0000000..35af293
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Plus.cs
@@ -0,0 +1,23 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Plus is an ability that boosts Special Attack if another ally has Plus or Minus.
+///
+/// Bulbapedia - Plus
+///
+[Script(ScriptCategory.Ability, "plus")]
+public class Plus : Script
+{
+ ///
+ public override void ChangeOffensiveStatValue(IExecutingMove move, IPokemon target, byte hit, uint defensiveStat,
+ ImmutableStatisticSet targetStats, Statistic stat, ref uint value)
+ {
+ var battleData = move.User.BattleData;
+ if (battleData is null)
+ return;
+ if (battleData.BattleSide.Pokemon.WhereNotNull().Any(x => x.IsUsable && x.ActiveAbility?.Name == "minus"))
+ {
+ value = value.MultiplyOrMax(1.5f);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PoisonHeal.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PoisonHeal.cs
new file mode 100644
index 0000000..17ff511
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PoisonHeal.cs
@@ -0,0 +1,20 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Poison Heal is an ability that heals the Pokémon when it is poisoned instead of damaging it.
+///
+/// Bulbapedia - Poison Heal
+///
+[Script(ScriptCategory.Ability, "poison_heal")]
+public class PoisonHeal : Script
+{
+ ///
+ public override void CustomTrigger(StringKey eventName, ICustomTriggerArgs args)
+ {
+ if (eventName != CustomTriggers.PoisonedDamage)
+ return;
+ if (args is not CustomTriggers.PoisonedDamageArgs poisonArgs)
+ return;
+ poisonArgs.Invert = true;
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PoisonPoint.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PoisonPoint.cs
new file mode 100644
index 0000000..0a16af0
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PoisonPoint.cs
@@ -0,0 +1,17 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Poison Point is an ability that may poison attackers making contact with the Pokémon.
+///
+/// Bulbapedia - Poison Point
+///
+[Script(ScriptCategory.Ability, "poison_point")]
+public class PoisonPoint : Script
+{
+ ///
+ public override void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit)
+ {
+ if (move.GetHitData(target, hit).IsContact)
+ move.User.SetStatus(ScriptUtils.ResolveName(), false);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PoisonTouch.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PoisonTouch.cs
new file mode 100644
index 0000000..f9b27f7
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PoisonTouch.cs
@@ -0,0 +1,19 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Poison Touch is an ability that may poison targets when the Pokémon uses a contact move.
+///
+/// Bulbapedia - Poison Touch
+///
+[Script(ScriptCategory.Ability, "poison_touch")]
+public class PoisonTouch : Script
+{
+ private const int PoisonChance = 30;
+
+ ///
+ public override void OnIncomingHit(IExecutingMove move, IPokemon target, byte hit)
+ {
+ if (move.GetHitData(target, hit).IsContact && move.Battle.Random.GetInt(0, 100) < PoisonChance)
+ move.User.SetStatus(ScriptUtils.ResolveName(), false);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PowerConstruct.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PowerConstruct.cs
new file mode 100644
index 0000000..e3fc87e
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PowerConstruct.cs
@@ -0,0 +1,37 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Power Construct is an ability that allows Zygarde to change form when its HP drops below half.
+///
+/// Bulbapedia - Power Construct
+///
+[Script(ScriptCategory.Ability, "power_construct")]
+public class PowerConstruct : Script
+{
+ private IPokemon? _pokemon;
+
+ ///
+ public override void OnAddedToParent(IScriptSource source)
+ {
+ if (source is not IPokemon pokemon)
+ throw new InvalidOperationException("PowerConstruct script must be attached to a Pokemon.");
+ _pokemon = pokemon;
+ }
+
+ ///
+ public override void OnEndTurn(IBattle battle)
+ {
+ if (_pokemon?.BattleData?.Battle == null)
+ return;
+ if (_pokemon.Species.Name != "zygarde")
+ return;
+ if (_pokemon.CurrentHealth > _pokemon.BoostedStats.Hp / 2)
+ return;
+ if (_pokemon.Form.Name != "10" || _pokemon.Form.Name != "50")
+ return;
+ if (!_pokemon.Species.TryGetForm("complete", out var completeForm))
+ return;
+
+ _pokemon.ChangeForm(completeForm);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PowerOfAlchemy.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PowerOfAlchemy.cs
new file mode 100644
index 0000000..3614b5c
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/PowerOfAlchemy.cs
@@ -0,0 +1,20 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Power of Alchemy is an ability that copies the ability of a fainted ally.
+///
+/// Bulbapedia - Power of Alchemy
+///
+[Script(ScriptCategory.Ability, "power_of_alchemy")]
+public class PowerOfAlchemy : Script
+{
+ ///
+ public override void OnAllyFaint(IPokemon ally, IPokemon faintedPokemon)
+ {
+ if (faintedPokemon.ActiveAbility?.HasFlag("cant_be_copied") != true)
+ return;
+
+ ally.BattleData?.Battle.EventHook.Invoke(new AbilityTriggerEvent(ally));
+ ally.ChangeAbility(faintedPokemon.ActiveAbility);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Prankster.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Prankster.cs
new file mode 100644
index 0000000..32d43d6
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Prankster.cs
@@ -0,0 +1,19 @@
+using PkmnLib.Static.Moves;
+
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Prankster is an ability that gives priority to status moves.
+///
+/// Bulbapedia - Prankster
+///
+[Script(ScriptCategory.Ability, "prankster")]
+public class Prankster : Script
+{
+ ///
+ public override void ChangePriority(IMoveChoice choice, ref sbyte priority)
+ {
+ if (choice.ChosenMove.MoveData.Category == MoveCategory.Status && priority != sbyte.MaxValue)
+ priority++;
+ }
+}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Pressure.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Pressure.cs
new file mode 100644
index 0000000..c2b2eed
--- /dev/null
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Abilities/Pressure.cs
@@ -0,0 +1,18 @@
+namespace PkmnLib.Plugin.Gen7.Scripts.Abilities;
+
+///
+/// Pressure is an ability that makes the opponent use more PP when using moves against the Pokémon.
+///
+/// Bulbapedia - Pressure
+///
+[Script(ScriptCategory.Ability, "pressure")]
+public class Pressure : Script
+{
+ ///
+ public override void ModifyPPUsedForIncomingMove(IExecutingMove executingMove, ref byte ppUsed)
+ {
+ if (ppUsed == byte.MaxValue)
+ return;
+ ppUsed++;
+ }
+}
\ 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 477f58d..4c7d6d6 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/CustomTriggers.cs
@@ -108,4 +108,12 @@ public static class CustomTriggers
{
public float HealPercent { get; set; } = HealPercent;
}
+
+ public static readonly StringKey PoisonedDamage = "poisoned_damage";
+
+ public record PoisonedDamageArgs(IPokemon Pokemon, uint Damage) : ICustomTriggerArgs
+ {
+ public uint Damage { get; set; } = Damage;
+ public bool Invert { get; set; } = false;
+ }
}
\ No newline at end of file
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Poisoned.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Poisoned.cs
index 3d5129c..8b6786c 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Poisoned.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Status/Poisoned.cs
@@ -28,6 +28,9 @@ public class Poisoned : Script
damage = 1;
var battleData = _pokemon.BattleData;
+ var args = new CustomTriggers.PoisonedDamageArgs(_pokemon, damage);
+ _pokemon.RunScriptHook(x => x.CustomTrigger(CustomTriggers.PoisonedDamage, args));
+
var eventBatchId = new EventBatchId();
battleData?.Battle.EventHook.Invoke(new DialogEvent("poisoned_damage", new Dictionary
{
@@ -37,6 +40,10 @@ public class Poisoned : Script
{
BatchId = eventBatchId,
});
- _pokemon.Damage(damage, DamageSource.Status, eventBatchId);
+
+ if (args.Invert)
+ _pokemon.Heal(damage, batchId: eventBatchId);
+ else
+ _pokemon.Damage(damage, DamageSource.Status, eventBatchId);
}
}
\ No newline at end of file