diff --git a/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs b/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs
index 9e88cb2..6648f85 100644
--- a/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs
+++ b/PkmnLib.Dynamic/BattleFlow/MoveTurnExecutor.cs
@@ -189,7 +189,8 @@ public static class MoveTurnExecutor
var basePower = battle.Library.DamageCalculator.GetBasePower(executingMove, target, hitIndex, hitData);
hitData.BasePower = basePower;
- hitData.Damage = battle.Library.DamageCalculator.GetDamage(executingMove, target, hitIndex, hitData);
+ hitData.Damage = battle.Library.DamageCalculator.GetDamage(executingMove, executingMove.UseMove.Category,
+ executingMove.User, target, executingMove.TargetCount, hitIndex, hitData);
var accuracy = useMove.Accuracy;
// If the accuracy is 255, the move should always hit, and as such we should not allow
diff --git a/PkmnLib.Dynamic/Libraries/DamageCalculator.cs b/PkmnLib.Dynamic/Libraries/DamageCalculator.cs
index c589e54..93f3590 100644
--- a/PkmnLib.Dynamic/Libraries/DamageCalculator.cs
+++ b/PkmnLib.Dynamic/Libraries/DamageCalculator.cs
@@ -1,4 +1,5 @@
using PkmnLib.Dynamic.Models;
+using PkmnLib.Static.Moves;
namespace PkmnLib.Dynamic.Libraries;
@@ -10,7 +11,8 @@ public interface IDamageCalculator
///
/// Calculate the damage for a given hit on a Pokemon.
///
- uint GetDamage(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData);
+ uint GetDamage(IExecutingMove? executingMove, MoveCategory category, IPokemon user, IPokemon target,
+ int targetCount, byte hitNumber, IHitData hitData);
///
/// Calculate the base power for a given hit on a Pokemon.
diff --git a/Plugins/PkmnLib.Plugin.Gen7.Tests/DamageCalculatorTests.cs b/Plugins/PkmnLib.Plugin.Gen7.Tests/DamageCalculatorTests.cs
index 4dc92e7..3ed9f1e 100644
--- a/Plugins/PkmnLib.Plugin.Gen7.Tests/DamageCalculatorTests.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7.Tests/DamageCalculatorTests.cs
@@ -56,7 +56,8 @@ public class DamageCalculatorTests
// has a double weakness to the move's Ice type
hit.Effectiveness.Returns(4.0f);
- var damage = damageCalculator.GetDamage(executingMove, defender, 0, hit);
+ var damage = damageCalculator.GetDamage(executingMove, executingMove.UseMove.Category, executingMove.User,
+ defender, executingMove.TargetCount, 0, hit);
// That means Ice Fang will do between 168 and 196 HP damage, depending on luck.
// Note that we are testing deterministic damage, so we expect the maximum damage.
await Assert.That(damage).IsEqualTo((uint)196);
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Libraries/Battling/Gen7DamageCalculator.cs b/Plugins/PkmnLib.Plugin.Gen7/Libraries/Battling/Gen7DamageCalculator.cs
index d2d0a63..8b663c5 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Libraries/Battling/Gen7DamageCalculator.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Libraries/Battling/Gen7DamageCalculator.cs
@@ -6,30 +6,30 @@ namespace PkmnLib.Plugin.Gen7.Libraries.Battling;
public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDamageCalculator
{
///
- public uint GetDamage(IExecutingMove executingMove, IPokemon target, byte hitNumber, IHitData hitData)
+ public uint GetDamage(IExecutingMove? executingMove, MoveCategory category, IPokemon user, IPokemon target,
+ int targetCount, byte hitNumber, IHitData hitData)
{
- var category = executingMove.UseMove.Category;
if (category == MoveCategory.Status)
return 0;
if (hitData.Effectiveness == 0)
return 0;
- var levelModifier = 2.0f * executingMove.User.Level / 5.0f + 2.0f;
+ var levelModifier = 2.0f * user.Level / 5.0f + 2.0f;
var basePower = (float)hitData.BasePower;
- var statModifier = GetStatModifier(executingMove, target, hitNumber, hitData);
- var damageModifier = GetDamageModifier(executingMove, target, hitNumber);
+ var statModifier = GetStatModifier(executingMove, category, user, target, hitNumber, hitData);
+ var damageModifier = executingMove == null ? 1.0f : GetDamageModifier(executingMove, target, hitNumber);
var floatDamage = MathF.Floor(levelModifier * basePower);
floatDamage = MathF.Floor(floatDamage * statModifier);
floatDamage = MathF.Floor(floatDamage / 50.0f) + 2.0f;
floatDamage = MathF.Floor(floatDamage * damageModifier);
- if (executingMove.TargetCount > 1)
+ if (targetCount > 1)
floatDamage = MathF.Floor(floatDamage * 0.75f);
if (hitData.IsCritical)
{
var critModifier = 1.5f;
- executingMove.RunScriptHook(script =>
+ executingMove?.RunScriptHook(script =>
script.ChangeCriticalModifier(executingMove, target, hitNumber, ref critModifier));
floatDamage = MathF.Floor(floatDamage * critModifier);
}
@@ -46,12 +46,12 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
var stabModifier = 1.0f;
var isStab = false;
- if (hitData.Type != null && executingMove.User.Types.Contains(hitData.Type.Value))
+ if (hitData.Type != null && user.Types.Contains(hitData.Type.Value))
{
stabModifier = 1.5f;
isStab = true;
}
- executingMove.RunScriptHook(script =>
+ executingMove?.RunScriptHook(script =>
script.ChangeStabModifier(executingMove, target, hitNumber, isStab, ref stabModifier));
floatDamage = MathF.Floor(floatDamage * stabModifier);
@@ -62,8 +62,14 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
< 1 => 1,
_ => (uint)floatDamage,
};
- executingMove.RunScriptHook(script => script.ChangeMoveDamage(executingMove, target, hitNumber, ref damage));
- target.RunScriptHook(script => script.ChangeIncomingMoveDamage(executingMove, target, hitNumber, ref damage));
+
+ if (executingMove is not null)
+ {
+ executingMove.RunScriptHook(script =>
+ script.ChangeMoveDamage(executingMove, target, hitNumber, ref damage));
+ target.RunScriptHook(script =>
+ script.ChangeIncomingMoveDamage(executingMove, target, hitNumber, ref damage));
+ }
return damage;
}
@@ -97,10 +103,9 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
};
}
- private static float GetStatModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber,
- IHitData hitData)
+ private static float GetStatModifier(IExecutingMove? executingMove, MoveCategory category, IPokemon user,
+ IPokemon target, byte hitNumber, IHitData hitData)
{
- var category = executingMove.UseMove.Category;
if (category == MoveCategory.Status)
return 1;
@@ -114,19 +119,19 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
// move is critical, and the target has a defensive stat boost of > 0, but a script is
// allowed to change this.
var bypassDefense = hitData.IsCritical && target.StatBoost.GetStatistic(defensive) > 0;
- executingMove.RunScriptHook(script =>
+ executingMove?.RunScriptHook(script =>
script.BypassDefensiveStatBoosts(executingMove, target, hitNumber, ref bypassDefense));
// Check if we can bypass the offensive stat boost on the user. We default to this if the
// move is critical, and the user has an offensive stat boost of < 0, but a script is
// allowed to change this.
- var bypassOffense = hitData.IsCritical && executingMove.User.StatBoost.GetStatistic(offensive) < 0;
- executingMove.RunScriptHook(script =>
+ var bypassOffense = hitData.IsCritical && user.StatBoost.GetStatistic(offensive) < 0;
+ executingMove?.RunScriptHook(script =>
script.BypassOffensiveStatBoosts(executingMove, target, hitNumber, ref bypassOffense));
- var userStats = executingMove.User.BoostedStats;
+ var userStats = user.BoostedStats;
if (bypassOffense)
- userStats = executingMove.User.FlatStats;
+ userStats = user.FlatStats;
var offensiveStat = userStats.GetStatistic(offensive);
var targetStats = target.BoostedStats;
@@ -135,17 +140,20 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
var defensiveStat = targetStats.GetStatistic(defensive);
var origOffensiveStat = offensiveStat;
- executingMove.RunScriptHook(script => script.ChangeOffensiveStatValue(executingMove, target, hitNumber,
- defensiveStat, targetStats, offensive, ref offensiveStat));
- executingMove.RunScriptHook(script => script.ChangeDefensiveStatValue(executingMove, target, hitNumber,
- origOffensiveStat, targetStats, defensive, ref defensiveStat));
- target.RunScriptHook(script => script.ChangeIncomingMoveOffensiveStatValue(executingMove, target, hitNumber,
- defensiveStat, targetStats, offensive, ref offensiveStat));
- target.RunScriptHook(script => script.ChangeIncomingMoveDefensiveStatValue(executingMove, target, hitNumber,
- origOffensiveStat, targetStats, defensive, ref defensiveStat));
+ if (executingMove != null)
+ {
+ executingMove.RunScriptHook(script => script.ChangeOffensiveStatValue(executingMove, target, hitNumber,
+ defensiveStat, targetStats, offensive, ref offensiveStat));
+ executingMove.RunScriptHook(script => script.ChangeDefensiveStatValue(executingMove, target, hitNumber,
+ origOffensiveStat, targetStats, defensive, ref defensiveStat));
+ target.RunScriptHook(script => script.ChangeIncomingMoveOffensiveStatValue(executingMove, target, hitNumber,
+ defensiveStat, targetStats, offensive, ref offensiveStat));
+ target.RunScriptHook(script => script.ChangeIncomingMoveDefensiveStatValue(executingMove, target, hitNumber,
+ origOffensiveStat, targetStats, defensive, ref defensiveStat));
+ }
var modifier = (float)offensiveStat / defensiveStat;
- executingMove.RunScriptHook(script =>
+ executingMove?.RunScriptHook(script =>
script.ChangeDamageStatModifier(executingMove, target, hitNumber, ref modifier));
return modifier;
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/FutureSightEffect.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/FutureSightEffect.cs
index c294700..e724b56 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/FutureSightEffect.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Battle/FutureSightEffect.cs
@@ -27,7 +27,8 @@ public class FutureSightEffect : Script
var executingMove = new ExecutingMoveImpl([target], 1, _moveChoice.ChosenMove,
_moveChoice.ChosenMove.MoveData, _moveChoice, battle);
var hitData = executingMove.GetHitData(target, 0);
- var damage = damageCalculator.GetDamage(executingMove, target, 1, hitData);
+ var damage = damageCalculator.GetDamage(executingMove, executingMove.UseMove.Category, executingMove.User,
+ target, executingMove.TargetCount, 1, hitData);
target.Damage(damage, DamageSource.Misc);
}
diff --git a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/Confusion.cs b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/Confusion.cs
index 6e35836..d310416 100644
--- a/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/Confusion.cs
+++ b/Plugins/PkmnLib.Plugin.Gen7/Scripts/Pokemon/Confusion.cs
@@ -1,7 +1,98 @@
+using PkmnLib.Static.Moves;
+
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
[Script(ScriptCategory.Pokemon, "confusion")]
public class Confusion : Script
{
- // TODO: Implement confusion
+ private int _turnsConfused;
+
+ ///
+ public override void OnAddedToParent(IScriptSource source)
+ {
+ if (source is not IPokemon pokemon)
+ throw new InvalidOperationException("Confusion script can only be added to a Pokemon.");
+ var battleData = pokemon.BattleData;
+ if (battleData == null)
+ throw new InvalidOperationException("Confusion script requires a Pokemon to be in a battle.");
+ _turnsConfused = battleData.Battle.Random.GetInt(2, 5);
+ }
+
+ ///
+ public override void StopBeforeMove(IExecutingMove move, ref bool stop)
+ {
+ _turnsConfused--;
+ move.Battle.EventHook.Invoke(new DialogEvent("pokemon_is_confused", new Dictionary
+ {
+ { "pokemon", move.User },
+ }));
+ if (_turnsConfused <= 0)
+ {
+ RemoveSelf();
+ move.Battle.EventHook.Invoke(new DialogEvent("pokemon_snapped_out_of_confusion",
+ new Dictionary
+ {
+ { "pokemon", move.User },
+ }));
+ return;
+ }
+
+ if (move.Battle.Random.GetInt(0, 100) < 33)
+ {
+ var damage = move.Battle.Library.DamageCalculator.GetDamage(null, MoveCategory.Physical, move.User,
+ move.User, 1, 0, new ConfusionHitData());
+
+ EventBatchId batchId = new();
+
+ move.User.Damage(damage, DamageSource.Confusion, batchId);
+ move.Battle.EventHook.Invoke(new DialogEvent("pokemon_hit_itself_in_confusion",
+ new Dictionary
+ {
+ { "pokemon", move.User },
+ { "damage", damage },
+ })
+ {
+ BatchId = batchId,
+ });
+
+ stop = true;
+ }
+ }
+
+ private class ConfusionHitData : IHitData
+ {
+ ///
+ public bool IsCritical => false;
+
+ ///
+ public ushort BasePower => 40;
+
+ ///
+ public float Effectiveness => 1.0f;
+
+ ///
+ public uint Damage { get; } = 0;
+
+ ///
+ public TypeIdentifier? Type => null;
+
+ ///
+ public bool IsContact => true;
+
+ ///
+ public bool HasFailed => false;
+
+ ///
+ public void Fail()
+ {
+ }
+
+ ///
+ public void SetFlag(StringKey flag)
+ {
+ }
+
+ ///
+ public bool HasFlag(StringKey flag) => false;
+ }
}
\ No newline at end of file