This commit is contained in:
parent
6394f4eab3
commit
6d448e4e8d
@ -189,7 +189,8 @@ public static class MoveTurnExecutor
|
|||||||
var basePower = battle.Library.DamageCalculator.GetBasePower(executingMove, target, hitIndex, hitData);
|
var basePower = battle.Library.DamageCalculator.GetBasePower(executingMove, target, hitIndex, hitData);
|
||||||
hitData.BasePower = basePower;
|
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;
|
var accuracy = useMove.Accuracy;
|
||||||
// If the accuracy is 255, the move should always hit, and as such we should not allow
|
// If the accuracy is 255, the move should always hit, and as such we should not allow
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using PkmnLib.Dynamic.Models;
|
using PkmnLib.Dynamic.Models;
|
||||||
|
using PkmnLib.Static.Moves;
|
||||||
|
|
||||||
namespace PkmnLib.Dynamic.Libraries;
|
namespace PkmnLib.Dynamic.Libraries;
|
||||||
|
|
||||||
@ -10,7 +11,8 @@ public interface IDamageCalculator
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculate the damage for a given hit on a Pokemon.
|
/// Calculate the damage for a given hit on a Pokemon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
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);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculate the base power for a given hit on a Pokemon.
|
/// Calculate the base power for a given hit on a Pokemon.
|
||||||
|
@ -56,7 +56,8 @@ public class DamageCalculatorTests
|
|||||||
// has a double weakness to the move's Ice type
|
// has a double weakness to the move's Ice type
|
||||||
hit.Effectiveness.Returns(4.0f);
|
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.
|
// 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.
|
// Note that we are testing deterministic damage, so we expect the maximum damage.
|
||||||
await Assert.That(damage).IsEqualTo((uint)196);
|
await Assert.That(damage).IsEqualTo((uint)196);
|
||||||
|
@ -6,30 +6,30 @@ namespace PkmnLib.Plugin.Gen7.Libraries.Battling;
|
|||||||
public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDamageCalculator
|
public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDamageCalculator
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
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)
|
if (category == MoveCategory.Status)
|
||||||
return 0;
|
return 0;
|
||||||
if (hitData.Effectiveness == 0)
|
if (hitData.Effectiveness == 0)
|
||||||
return 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 basePower = (float)hitData.BasePower;
|
||||||
var statModifier = GetStatModifier(executingMove, target, hitNumber, hitData);
|
var statModifier = GetStatModifier(executingMove, category, user, target, hitNumber, hitData);
|
||||||
var damageModifier = GetDamageModifier(executingMove, target, hitNumber);
|
var damageModifier = executingMove == null ? 1.0f : GetDamageModifier(executingMove, target, hitNumber);
|
||||||
|
|
||||||
var floatDamage = MathF.Floor(levelModifier * basePower);
|
var floatDamage = MathF.Floor(levelModifier * basePower);
|
||||||
floatDamage = MathF.Floor(floatDamage * statModifier);
|
floatDamage = MathF.Floor(floatDamage * statModifier);
|
||||||
floatDamage = MathF.Floor(floatDamage / 50.0f) + 2.0f;
|
floatDamage = MathF.Floor(floatDamage / 50.0f) + 2.0f;
|
||||||
floatDamage = MathF.Floor(floatDamage * damageModifier);
|
floatDamage = MathF.Floor(floatDamage * damageModifier);
|
||||||
if (executingMove.TargetCount > 1)
|
if (targetCount > 1)
|
||||||
floatDamage = MathF.Floor(floatDamage * 0.75f);
|
floatDamage = MathF.Floor(floatDamage * 0.75f);
|
||||||
|
|
||||||
if (hitData.IsCritical)
|
if (hitData.IsCritical)
|
||||||
{
|
{
|
||||||
var critModifier = 1.5f;
|
var critModifier = 1.5f;
|
||||||
executingMove.RunScriptHook(script =>
|
executingMove?.RunScriptHook(script =>
|
||||||
script.ChangeCriticalModifier(executingMove, target, hitNumber, ref critModifier));
|
script.ChangeCriticalModifier(executingMove, target, hitNumber, ref critModifier));
|
||||||
floatDamage = MathF.Floor(floatDamage * critModifier);
|
floatDamage = MathF.Floor(floatDamage * critModifier);
|
||||||
}
|
}
|
||||||
@ -46,12 +46,12 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
|
|||||||
|
|
||||||
var stabModifier = 1.0f;
|
var stabModifier = 1.0f;
|
||||||
var isStab = false;
|
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;
|
stabModifier = 1.5f;
|
||||||
isStab = true;
|
isStab = true;
|
||||||
}
|
}
|
||||||
executingMove.RunScriptHook(script =>
|
executingMove?.RunScriptHook(script =>
|
||||||
script.ChangeStabModifier(executingMove, target, hitNumber, isStab, ref stabModifier));
|
script.ChangeStabModifier(executingMove, target, hitNumber, isStab, ref stabModifier));
|
||||||
floatDamage = MathF.Floor(floatDamage * stabModifier);
|
floatDamage = MathF.Floor(floatDamage * stabModifier);
|
||||||
|
|
||||||
@ -62,8 +62,14 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
|
|||||||
< 1 => 1,
|
< 1 => 1,
|
||||||
_ => (uint)floatDamage,
|
_ => (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;
|
return damage;
|
||||||
}
|
}
|
||||||
@ -97,10 +103,9 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float GetStatModifier(IExecutingMove executingMove, IPokemon target, byte hitNumber,
|
private static float GetStatModifier(IExecutingMove? executingMove, MoveCategory category, IPokemon user,
|
||||||
IHitData hitData)
|
IPokemon target, byte hitNumber, IHitData hitData)
|
||||||
{
|
{
|
||||||
var category = executingMove.UseMove.Category;
|
|
||||||
if (category == MoveCategory.Status)
|
if (category == MoveCategory.Status)
|
||||||
return 1;
|
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
|
// move is critical, and the target has a defensive stat boost of > 0, but a script is
|
||||||
// allowed to change this.
|
// allowed to change this.
|
||||||
var bypassDefense = hitData.IsCritical && target.StatBoost.GetStatistic(defensive) > 0;
|
var bypassDefense = hitData.IsCritical && target.StatBoost.GetStatistic(defensive) > 0;
|
||||||
executingMove.RunScriptHook(script =>
|
executingMove?.RunScriptHook(script =>
|
||||||
script.BypassDefensiveStatBoosts(executingMove, target, hitNumber, ref bypassDefense));
|
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
|
// 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
|
// move is critical, and the user has an offensive stat boost of < 0, but a script is
|
||||||
// allowed to change this.
|
// allowed to change this.
|
||||||
var bypassOffense = hitData.IsCritical && executingMove.User.StatBoost.GetStatistic(offensive) < 0;
|
var bypassOffense = hitData.IsCritical && user.StatBoost.GetStatistic(offensive) < 0;
|
||||||
executingMove.RunScriptHook(script =>
|
executingMove?.RunScriptHook(script =>
|
||||||
script.BypassOffensiveStatBoosts(executingMove, target, hitNumber, ref bypassOffense));
|
script.BypassOffensiveStatBoosts(executingMove, target, hitNumber, ref bypassOffense));
|
||||||
|
|
||||||
var userStats = executingMove.User.BoostedStats;
|
var userStats = user.BoostedStats;
|
||||||
if (bypassOffense)
|
if (bypassOffense)
|
||||||
userStats = executingMove.User.FlatStats;
|
userStats = user.FlatStats;
|
||||||
var offensiveStat = userStats.GetStatistic(offensive);
|
var offensiveStat = userStats.GetStatistic(offensive);
|
||||||
|
|
||||||
var targetStats = target.BoostedStats;
|
var targetStats = target.BoostedStats;
|
||||||
@ -135,17 +140,20 @@ public class Gen7DamageCalculator(Gen7PluginConfiguration configuration) : IDama
|
|||||||
var defensiveStat = targetStats.GetStatistic(defensive);
|
var defensiveStat = targetStats.GetStatistic(defensive);
|
||||||
var origOffensiveStat = offensiveStat;
|
var origOffensiveStat = offensiveStat;
|
||||||
|
|
||||||
executingMove.RunScriptHook(script => script.ChangeOffensiveStatValue(executingMove, target, hitNumber,
|
if (executingMove != null)
|
||||||
defensiveStat, targetStats, offensive, ref offensiveStat));
|
{
|
||||||
executingMove.RunScriptHook(script => script.ChangeDefensiveStatValue(executingMove, target, hitNumber,
|
executingMove.RunScriptHook(script => script.ChangeOffensiveStatValue(executingMove, target, hitNumber,
|
||||||
origOffensiveStat, targetStats, defensive, ref defensiveStat));
|
defensiveStat, targetStats, offensive, ref offensiveStat));
|
||||||
target.RunScriptHook(script => script.ChangeIncomingMoveOffensiveStatValue(executingMove, target, hitNumber,
|
executingMove.RunScriptHook(script => script.ChangeDefensiveStatValue(executingMove, target, hitNumber,
|
||||||
defensiveStat, targetStats, offensive, ref offensiveStat));
|
origOffensiveStat, targetStats, defensive, ref defensiveStat));
|
||||||
target.RunScriptHook(script => script.ChangeIncomingMoveDefensiveStatValue(executingMove, target, hitNumber,
|
target.RunScriptHook(script => script.ChangeIncomingMoveOffensiveStatValue(executingMove, target, hitNumber,
|
||||||
origOffensiveStat, targetStats, defensive, ref defensiveStat));
|
defensiveStat, targetStats, offensive, ref offensiveStat));
|
||||||
|
target.RunScriptHook(script => script.ChangeIncomingMoveDefensiveStatValue(executingMove, target, hitNumber,
|
||||||
|
origOffensiveStat, targetStats, defensive, ref defensiveStat));
|
||||||
|
}
|
||||||
|
|
||||||
var modifier = (float)offensiveStat / defensiveStat;
|
var modifier = (float)offensiveStat / defensiveStat;
|
||||||
executingMove.RunScriptHook(script =>
|
executingMove?.RunScriptHook(script =>
|
||||||
script.ChangeDamageStatModifier(executingMove, target, hitNumber, ref modifier));
|
script.ChangeDamageStatModifier(executingMove, target, hitNumber, ref modifier));
|
||||||
|
|
||||||
return modifier;
|
return modifier;
|
||||||
|
@ -27,7 +27,8 @@ public class FutureSightEffect : Script
|
|||||||
var executingMove = new ExecutingMoveImpl([target], 1, _moveChoice.ChosenMove,
|
var executingMove = new ExecutingMoveImpl([target], 1, _moveChoice.ChosenMove,
|
||||||
_moveChoice.ChosenMove.MoveData, _moveChoice, battle);
|
_moveChoice.ChosenMove.MoveData, _moveChoice, battle);
|
||||||
var hitData = executingMove.GetHitData(target, 0);
|
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);
|
target.Damage(damage, DamageSource.Misc);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,98 @@
|
|||||||
|
using PkmnLib.Static.Moves;
|
||||||
|
|
||||||
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
namespace PkmnLib.Plugin.Gen7.Scripts.Pokemon;
|
||||||
|
|
||||||
[Script(ScriptCategory.Pokemon, "confusion")]
|
[Script(ScriptCategory.Pokemon, "confusion")]
|
||||||
public class Confusion : Script
|
public class Confusion : Script
|
||||||
{
|
{
|
||||||
// TODO: Implement confusion
|
private int _turnsConfused;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void StopBeforeMove(IExecutingMove move, ref bool stop)
|
||||||
|
{
|
||||||
|
_turnsConfused--;
|
||||||
|
move.Battle.EventHook.Invoke(new DialogEvent("pokemon_is_confused", new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "pokemon", move.User },
|
||||||
|
}));
|
||||||
|
if (_turnsConfused <= 0)
|
||||||
|
{
|
||||||
|
RemoveSelf();
|
||||||
|
move.Battle.EventHook.Invoke(new DialogEvent("pokemon_snapped_out_of_confusion",
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "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<string, object>
|
||||||
|
{
|
||||||
|
{ "pokemon", move.User },
|
||||||
|
{ "damage", damage },
|
||||||
|
})
|
||||||
|
{
|
||||||
|
BatchId = batchId,
|
||||||
|
});
|
||||||
|
|
||||||
|
stop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConfusionHitData : IHitData
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsCritical => false;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ushort BasePower => 40;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public float Effectiveness => 1.0f;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public uint Damage { get; } = 0;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public TypeIdentifier? Type => null;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsContact => true;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool HasFailed => false;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Fail()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void SetFlag(StringKey flag)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool HasFlag(StringKey flag) => false;
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user