This commit is contained in:
@@ -42,6 +42,11 @@ public enum MoveLearnMethod
|
|||||||
/// The move is learned by using a move sketch.
|
/// The move is learned by using a move sketch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Sketch,
|
Sketch,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The move is temporarily copied through Mimic.
|
||||||
|
/// </summary>
|
||||||
|
Mimic,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -167,10 +167,18 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The moves the Pokemon has learned. This is of a set length of <see cref="Const.MovesCount"/>. Empty move slots
|
/// The moves the Pokemon has learned. This is of a set length of <see cref="Const.MovesCount"/>. Empty move slots
|
||||||
/// are null.
|
/// are null. If a move slot is temporarily replaced (see <see cref="LearnTemporaryMove"/>), this shows the
|
||||||
|
/// replacement move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyList<ILearnedMove?> Moves { get; }
|
IReadOnlyList<ILearnedMove?> Moves { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The permanently learned moves of the Pokemon, ignoring any temporary replacements made through
|
||||||
|
/// <see cref="LearnTemporaryMove"/>. This is of a set length of <see cref="Const.MovesCount"/>. Empty move
|
||||||
|
/// slots are null.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyList<ILearnedMove?> BaseMoves { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether the Pokemon has a specific move in its current moveset.
|
/// Checks whether the Pokemon has a specific move in its current moveset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -181,6 +189,14 @@ public interface IPokemon : IScriptSource, IDeepCloneable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
void SwapMoves(byte index1, byte index2);
|
void SwapMoves(byte index1, byte index2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Temporarily replaces the move in the given slot until the Pokemon leaves the battlefield. The permanently
|
||||||
|
/// learned move in the slot is left untouched and becomes visible again automatically. Used by effects such as
|
||||||
|
/// Mimic.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="KeyNotFoundException">Thrown when the move is not found in the move library.</exception>
|
||||||
|
void LearnTemporaryMove(StringKey moveName, MoveLearnMethod method, byte index);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not the Pokemon is allowed to gain experience.
|
/// Whether or not the Pokemon is allowed to gain experience.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -772,8 +788,19 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||||||
|
|
||||||
private readonly ILearnedMove?[] _learnedMoves = new ILearnedMove[Const.MovesCount];
|
private readonly ILearnedMove?[] _learnedMoves = new ILearnedMove[Const.MovesCount];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Battle-only per-slot overrides of <see cref="_learnedMoves"/>. The permanent moveset is never mutated by
|
||||||
|
/// temporary moves; discarding this array is all that is needed to restore the original moves.
|
||||||
|
/// </summary>
|
||||||
|
private ILearnedMove?[]? _temporaryMoves;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IReadOnlyList<ILearnedMove?> Moves => _learnedMoves;
|
public IReadOnlyList<ILearnedMove?> Moves => _temporaryMoves == null
|
||||||
|
? _learnedMoves
|
||||||
|
: _temporaryMoves.Select((move, index) => move ?? _learnedMoves[index]).ToArray();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IReadOnlyList<ILearnedMove?> BaseMoves => _learnedMoves;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool HasMove(StringKey moveName) => Moves.Any(move => move?.MoveData.Name == moveName);
|
public bool HasMove(StringKey moveName) => Moves.Any(move => move?.MoveData.Name == moveName);
|
||||||
@@ -783,10 +810,9 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||||||
{
|
{
|
||||||
if (index1 >= Const.MovesCount || index2 >= Const.MovesCount)
|
if (index1 >= Const.MovesCount || index2 >= Const.MovesCount)
|
||||||
return;
|
return;
|
||||||
var move1 = _learnedMoves[index1];
|
(_learnedMoves[index1], _learnedMoves[index2]) = (_learnedMoves[index2], _learnedMoves[index1]);
|
||||||
var move2 = _learnedMoves[index2];
|
if (_temporaryMoves != null)
|
||||||
_learnedMoves[index1] = move2;
|
(_temporaryMoves[index1], _temporaryMoves[index2]) = (_temporaryMoves[index2], _temporaryMoves[index1]);
|
||||||
_learnedMoves[index2] = move1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -1286,6 +1312,18 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||||||
_learnedMoves[index] = new LearnedMoveImpl(move, method);
|
_learnedMoves[index] = new LearnedMoveImpl(move, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void LearnTemporaryMove(StringKey moveName, MoveLearnMethod method, byte index)
|
||||||
|
{
|
||||||
|
if (index >= Const.MovesCount)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index), $"Move slot {index} is out of range.");
|
||||||
|
if (!Library.StaticLibrary.Moves.TryGet(moveName, out var move))
|
||||||
|
throw new KeyNotFoundException($"Move {moveName} not found.");
|
||||||
|
|
||||||
|
_temporaryMoves ??= new ILearnedMove?[Const.MovesCount];
|
||||||
|
_temporaryMoves[index] = new LearnedMoveImpl(move, method);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool HasStatus(StringKey status) => StatusScript.Script?.Name == status;
|
public bool HasStatus(StringKey status) => StatusScript.Script?.Name == status;
|
||||||
|
|
||||||
@@ -1367,6 +1405,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||||||
if (!onBattleField)
|
if (!onBattleField)
|
||||||
{
|
{
|
||||||
Volatile.Clear();
|
Volatile.Clear();
|
||||||
|
_temporaryMoves = null;
|
||||||
HeightInMeters = Form.Height;
|
HeightInMeters = Form.Height;
|
||||||
Types = Form.Types;
|
Types = Form.Types;
|
||||||
OverrideAbility = null;
|
OverrideAbility = null;
|
||||||
@@ -1388,6 +1427,7 @@ public class PokemonImpl : ScriptSource, IPokemon
|
|||||||
var battleData = BattleData;
|
var battleData = BattleData;
|
||||||
BattleData = null;
|
BattleData = null;
|
||||||
Volatile.Clear();
|
Volatile.Clear();
|
||||||
|
_temporaryMoves = null;
|
||||||
HeightInMeters = Form.Height;
|
HeightInMeters = Form.Height;
|
||||||
Types = Form.Types;
|
Types = Form.Types;
|
||||||
OverrideAbility = null;
|
OverrideAbility = null;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public record SerializedPokemon
|
|||||||
Nature = pokemon.Nature.Name;
|
Nature = pokemon.Nature.Name;
|
||||||
Nickname = pokemon.Nickname;
|
Nickname = pokemon.Nickname;
|
||||||
Ability = pokemon.Form.GetAbility(pokemon.AbilityIndex);
|
Ability = pokemon.Form.GetAbility(pokemon.AbilityIndex);
|
||||||
Moves = pokemon.Moves.Select(move =>
|
Moves = pokemon.BaseMoves.Select(move =>
|
||||||
{
|
{
|
||||||
if (move == null)
|
if (move == null)
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
82
PkmnLib.Tests/Dynamic/PokemonTemporaryMoveTests.cs
Normal file
82
PkmnLib.Tests/Dynamic/PokemonTemporaryMoveTests.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using PkmnLib.Dynamic.Models;
|
||||||
|
using PkmnLib.Static;
|
||||||
|
using PkmnLib.Static.Species;
|
||||||
|
using PkmnLib.Tests.Integration;
|
||||||
|
|
||||||
|
namespace PkmnLib.Tests.Dynamic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests for the temporary move overlay (<see cref="IPokemon.LearnTemporaryMove"/>), used by effects such as
|
||||||
|
/// Mimic. The permanently learned moves must never be mutated by a temporary move, and the overlay must be
|
||||||
|
/// discarded by the engine itself when the Pokemon leaves the battlefield.
|
||||||
|
/// </summary>
|
||||||
|
public class PokemonTemporaryMoveTests
|
||||||
|
{
|
||||||
|
private static IPokemon CreatePokemon()
|
||||||
|
{
|
||||||
|
var library = LibraryHelpers.LoadLibrary();
|
||||||
|
if (!library.StaticLibrary.Species.TryGet("bulbasaur", out var species))
|
||||||
|
throw new InvalidOperationException("Failed to load bulbasaur species.");
|
||||||
|
var pokemon = new PokemonImpl(library, species, species.GetDefaultForm(), new AbilityIndex
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
IsHidden = false,
|
||||||
|
}, 50, 0, Gender.Male, 0, "hardy");
|
||||||
|
pokemon.LearnMove("tackle", MoveLearnMethod.LevelUp, 0);
|
||||||
|
return pokemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task LearnTemporaryMove_ReplacesMoveInMovesButNotInBaseMoves()
|
||||||
|
{
|
||||||
|
var pokemon = CreatePokemon();
|
||||||
|
|
||||||
|
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
||||||
|
|
||||||
|
await Assert.That(pokemon.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("swords_dance");
|
||||||
|
await Assert.That(pokemon.Moves[0]!.LearnMethod).IsEqualTo(MoveLearnMethod.Mimic);
|
||||||
|
await Assert.That(pokemon.BaseMoves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task SetOnBattlefield_LeavingField_RestoresOriginalMoveWithItsPP()
|
||||||
|
{
|
||||||
|
var pokemon = CreatePokemon();
|
||||||
|
pokemon.SetBattleData(Substitute.For<IBattle>(), 0);
|
||||||
|
pokemon.SetOnBattlefield(true);
|
||||||
|
|
||||||
|
// Use the original move once, so we can verify its PP survives the temporary replacement untouched.
|
||||||
|
var originalMove = pokemon.Moves[0]!;
|
||||||
|
originalMove.TryUse();
|
||||||
|
var expectedPp = originalMove.CurrentPp;
|
||||||
|
|
||||||
|
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
||||||
|
pokemon.SetOnBattlefield(false);
|
||||||
|
|
||||||
|
await Assert.That(ReferenceEquals(pokemon.Moves[0], originalMove)).IsTrue();
|
||||||
|
await Assert.That(pokemon.Moves[0]!.CurrentPp).IsEqualTo(expectedPp);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task ClearBattleData_RestoresOriginalMove()
|
||||||
|
{
|
||||||
|
var pokemon = CreatePokemon();
|
||||||
|
pokemon.SetBattleData(Substitute.For<IBattle>(), 0);
|
||||||
|
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
||||||
|
|
||||||
|
pokemon.ClearBattleData();
|
||||||
|
|
||||||
|
await Assert.That(pokemon.Moves[0]!.MoveData.Name.ToString()).IsEqualTo("tackle");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Serialize_WithActiveTemporaryMove_WritesOriginalMove()
|
||||||
|
{
|
||||||
|
var pokemon = CreatePokemon();
|
||||||
|
pokemon.LearnTemporaryMove("swords_dance", MoveLearnMethod.Mimic, 0);
|
||||||
|
|
||||||
|
var serialized = pokemon.Serialize();
|
||||||
|
|
||||||
|
await Assert.That(serialized.Moves[0]!.MoveName.ToString()).IsEqualTo("tackle");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,10 +15,6 @@ namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class MimicTests
|
public class MimicTests
|
||||||
{
|
{
|
||||||
private const string NotImplementedReason =
|
|
||||||
"Mimic is not implemented: Mimic.cs contains only a FIXME comment and implements no script hooks, so " +
|
|
||||||
"the move has no effect (Bulbapedia: 'Mimic copies a move from the target')";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a mocked <see cref="ILearnedMove"/> whose move data has the given name.
|
/// Creates a mocked <see cref="ILearnedMove"/> whose move data has the given name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -46,9 +42,12 @@ public class MimicTests
|
|||||||
move.GetHitData(target, 0).Returns(hitData);
|
move.GetHitData(target, 0).Returns(hitData);
|
||||||
|
|
||||||
// The user knows another move in slot 0, and Mimic in slot 1; Mimic is the chosen move.
|
// The user knows another move in slot 0, and Mimic in slot 1; Mimic is the chosen move.
|
||||||
|
// The moves are created before the Returns call, as creating substitutes inside Returns arguments
|
||||||
|
// resets NSubstitute's last-call tracking.
|
||||||
var mimicMove = CreateLearnedMove("mimic");
|
var mimicMove = CreateLearnedMove("mimic");
|
||||||
move.ChosenMove.Returns(mimicMove);
|
move.ChosenMove.Returns(mimicMove);
|
||||||
user.Moves.Returns(new[] { CreateLearnedMove(userOtherMove), mimicMove });
|
var userMoves = new[] { CreateLearnedMove(userOtherMove), mimicMove };
|
||||||
|
user.Moves.Returns(userMoves);
|
||||||
|
|
||||||
var battleData = Substitute.For<IPokemonBattleData>();
|
var battleData = Substitute.For<IPokemonBattleData>();
|
||||||
if (targetLastUsedMove != null)
|
if (targetLastUsedMove != null)
|
||||||
@@ -85,11 +84,11 @@ public class MimicTests
|
|||||||
hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail");
|
hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to extract the arguments of the user's received <see cref="IPokemon.LearnMove"/> call.
|
/// Helper to extract the arguments of the user's received <see cref="IPokemon.LearnTemporaryMove"/> call.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static (StringKey moveName, byte index)? GetLearnedMove(IPokemon user)
|
private static (StringKey moveName, byte index)? GetLearnedMove(IPokemon user)
|
||||||
{
|
{
|
||||||
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "LearnMove");
|
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "LearnTemporaryMove");
|
||||||
if (call == null)
|
if (call == null)
|
||||||
return null;
|
return null;
|
||||||
return ((StringKey)call.GetArguments()[0]!, (byte)call.GetArguments()[2]!);
|
return ((StringKey)call.GetArguments()[0]!, (byte)call.GetArguments()[2]!);
|
||||||
@@ -100,7 +99,7 @@ public class MimicTests
|
|||||||
/// place" and (Generation II onwards) "Mimic now copies the target's last used move."
|
/// place" and (Generation II onwards) "Mimic now copies the target's last used move."
|
||||||
/// The target's last used move must be learned in the move slot that holds Mimic.
|
/// The target's last used move must be learned in the move slot that holds Mimic.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test, TestFailing(NotImplementedReason)]
|
[Test]
|
||||||
public async Task OnSecondaryEffect_TargetUsedMove_UserLearnsTargetsLastMoveInMimicsSlot()
|
public async Task OnSecondaryEffect_TargetUsedMove_UserLearnsTargetsLastMoveInMimicsSlot()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
@@ -121,7 +120,7 @@ public class MimicTests
|
|||||||
/// Bulbapedia: (Generation II onwards) "Mimic now copies the target's last used move."
|
/// Bulbapedia: (Generation II onwards) "Mimic now copies the target's last used move."
|
||||||
/// If the target has not used a move yet, there is nothing to copy and the hit must fail.
|
/// If the target has not used a move yet, there is nothing to copy and the hit must fail.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test, TestFailing(NotImplementedReason)]
|
[Test]
|
||||||
public async Task OnSecondaryEffect_TargetHasNotUsedAMove_FailsHit()
|
public async Task OnSecondaryEffect_TargetHasNotUsedAMove_FailsHit()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
@@ -141,8 +140,8 @@ public class MimicTests
|
|||||||
/// Metronome, or any move the user already knows.", (Generation IV onwards) "Mimic will fail to copy
|
/// Metronome, or any move the user already knows.", (Generation IV onwards) "Mimic will fail to copy
|
||||||
/// Chatter.", and (Generation V onwards) "Mimic is no longer able to copy Transform."
|
/// Chatter.", and (Generation V onwards) "Mimic is no longer able to copy Transform."
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test, TestFailing(NotImplementedReason), Arguments("sketch"), Arguments("transform"), Arguments("struggle"),
|
[Test, Arguments("sketch"), Arguments("transform"), Arguments("struggle"), Arguments("metronome"),
|
||||||
Arguments("metronome"), Arguments("chatter")]
|
Arguments("chatter")]
|
||||||
public async Task OnSecondaryEffect_TargetsLastMoveIsUncopyable_FailsHit(string uncopyableMove)
|
public async Task OnSecondaryEffect_TargetsLastMoveIsUncopyable_FailsHit(string uncopyableMove)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
@@ -160,7 +159,7 @@ public class MimicTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bulbapedia: (Generation II onwards) "It now fails to copy ... any move the user already knows."
|
/// Bulbapedia: (Generation II onwards) "It now fails to copy ... any move the user already knows."
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test, TestFailing(NotImplementedReason)]
|
[Test]
|
||||||
public async Task OnSecondaryEffect_UserAlreadyKnowsTargetsLastMove_FailsHit()
|
public async Task OnSecondaryEffect_UserAlreadyKnowsTargetsLastMove_FailsHit()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
@@ -175,4 +174,22 @@ public class MimicTests
|
|||||||
await Assert.That(ReceivedFail(hitData)).IsTrue();
|
await Assert.That(ReceivedFail(hitData)).IsTrue();
|
||||||
await Assert.That(GetLearnedMove(user).HasValue).IsFalse();
|
await Assert.That(GetLearnedMove(user).HasValue).IsFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bulbapedia: (Generation II onwards) "The copied move will also only have 5 PP".
|
||||||
|
/// After learning the copy, its current PP must be set to 5.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task OnSecondaryEffect_TargetUsedMove_CopiedMoveGetsFivePP()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mimic = await GetSecondaryEffectHook();
|
||||||
|
var (move, user, target, _) = CreateTestSetup("tackle");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
mimic.OnSecondaryEffect(move, target, 0);
|
||||||
|
|
||||||
|
// Assert - the move in Mimic's slot (slot 1) has its PP set to 5.
|
||||||
|
user.Moves[1]!.Received(1).SetCurrentPP(5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,43 @@
|
|||||||
|
using PkmnLib.Plugin.Gen7.Scripts.Utils;
|
||||||
|
|
||||||
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
namespace PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mimic copies the target's last used move into the move slot Mimic occupies, with 5 PP, until the user
|
||||||
|
/// leaves the field. It fails if the target has not used a move, if that move is Sketch, Transform, Struggle,
|
||||||
|
/// Metronome, or Chatter, or if the user already knows it.
|
||||||
|
///
|
||||||
|
/// <see href="https://bulbapedia.bulbagarden.net/wiki/Mimic_(move)">Bulbapedia - Mimic</see>
|
||||||
|
/// </summary>
|
||||||
[Script(ScriptCategory.Move, "mimic")]
|
[Script(ScriptCategory.Move, "mimic")]
|
||||||
public class Mimic : Script
|
public class Mimic : Script, IScriptOnSecondaryEffect
|
||||||
{
|
{
|
||||||
// FIXME: support for temporarily copying moves to a move slot.
|
/// <inheritdoc />
|
||||||
|
public void OnSecondaryEffect(IExecutingMove move, IPokemon target, byte hit)
|
||||||
|
{
|
||||||
|
var moveSlot = move.User.Moves.IndexOf(move.ChosenMove);
|
||||||
|
if (moveSlot == -1)
|
||||||
|
{
|
||||||
|
move.GetHitData(target, hit).Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastMove = target.BattleData?.LastMoveChoice;
|
||||||
|
if (lastMove == null || !lastMove.ChosenMove.MoveData.CanCopyMove())
|
||||||
|
{
|
||||||
|
move.GetHitData(target, hit).Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var copiedMoveName = lastMove.ChosenMove.MoveData.Name;
|
||||||
|
if (move.User.Moves.Any(m => m?.MoveData.Name == copiedMoveName))
|
||||||
|
{
|
||||||
|
move.GetHitData(target, hit).Fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
move.User.LearnTemporaryMove(copiedMoveName, MoveLearnMethod.Mimic, (byte)moveSlot);
|
||||||
|
// The copied move only has 5 PP, or its own maximum PP if that is lower.
|
||||||
|
move.User.Moves[moveSlot]?.SetCurrentPP(5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user