This commit is contained in:
283
Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/FacadeTests.cs
Normal file
283
Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/FacadeTests.cs
Normal file
@@ -0,0 +1,283 @@
|
||||
using PkmnLib.Dynamic.Models;
|
||||
using PkmnLib.Dynamic.ScriptHandling;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Status;
|
||||
using PkmnLib.Static.Moves;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the <see cref="Facade"/> move script.
|
||||
/// Gen VII Bulbapedia behavior: "Facade inflicts damage. Its base power doubles from 70 to 140 if the user is
|
||||
/// poisoned, paralyzed, or burned." Generation VI onwards: "When using Facade, Burn's effect of halving the
|
||||
/// damage done by physical moves is now ignored."
|
||||
/// </summary>
|
||||
public class FacadeTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a fully mocked test setup where the user has the given (or no) non-volatile status in its
|
||||
/// <see cref="IPokemon.StatusScript"/>, and is using a move of the given category.
|
||||
/// </summary>
|
||||
private static (Facade facade, IExecutingMove move, IPokemon target) CreateTestSetup(Script? status,
|
||||
MoveCategory category = MoveCategory.Physical)
|
||||
{
|
||||
var facade = new Facade();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
var target = Substitute.For<IPokemon>();
|
||||
var user = Substitute.For<IPokemon>();
|
||||
user.StatusScript.Returns(status == null ? new ScriptContainer() : new ScriptContainer(status));
|
||||
move.User.Returns(user);
|
||||
var useMove = Substitute.For<IMoveData>();
|
||||
useMove.Category.Returns(category);
|
||||
move.UseMove.Returns(useMove);
|
||||
return (facade, move, target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Its base power doubles from 70 to 140 if the user is poisoned, paralyzed, or burned."
|
||||
/// The user is burned.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_UserBurned_BasePowerDoubles()
|
||||
{
|
||||
// Arrange
|
||||
var (facade, move, target) = CreateTestSetup(new Burned());
|
||||
ushort basePower = 70;
|
||||
|
||||
// Act
|
||||
facade.ChangeBasePower(move, target, 0, ref basePower);
|
||||
|
||||
// Assert
|
||||
await Assert.That(basePower).IsEqualTo((ushort)140);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Its base power doubles from 70 to 140 if the user is poisoned, paralyzed, or burned."
|
||||
/// The user is paralyzed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_UserParalyzed_BasePowerDoubles()
|
||||
{
|
||||
// Arrange
|
||||
var (facade, move, target) = CreateTestSetup(new Paralyzed());
|
||||
ushort basePower = 70;
|
||||
|
||||
// Act
|
||||
facade.ChangeBasePower(move, target, 0, ref basePower);
|
||||
|
||||
// Assert
|
||||
await Assert.That(basePower).IsEqualTo((ushort)140);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Its base power doubles from 70 to 140 if the user is poisoned, paralyzed, or burned."
|
||||
/// The user is poisoned.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_UserPoisoned_BasePowerDoubles()
|
||||
{
|
||||
// Arrange
|
||||
var (facade, move, target) = CreateTestSetup(new Poisoned());
|
||||
ushort basePower = 70;
|
||||
|
||||
// Act
|
||||
facade.ChangeBasePower(move, target, 0, ref basePower);
|
||||
|
||||
// Assert
|
||||
await Assert.That(basePower).IsEqualTo((ushort)140);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Its base power doubles from 70 to 140 if the user is poisoned, paralyzed, or burned."
|
||||
/// Bad poison is a form of poison, so a badly poisoned user (<see cref="BadlyPoisoned"/>) also gets the
|
||||
/// doubled base power.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_UserBadlyPoisoned_BasePowerDoubles()
|
||||
{
|
||||
// Arrange
|
||||
var (facade, move, target) = CreateTestSetup(new BadlyPoisoned());
|
||||
ushort basePower = 70;
|
||||
|
||||
// Act
|
||||
facade.ChangeBasePower(move, target, 0, ref basePower);
|
||||
|
||||
// Assert
|
||||
await Assert.That(basePower).IsEqualTo((ushort)140);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: the base power only doubles "if the user is poisoned, paralyzed, or burned" — with no
|
||||
/// status the base power is unchanged.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_UserHasNoStatus_BasePowerUnchanged()
|
||||
{
|
||||
// Arrange
|
||||
var (facade, move, target) = CreateTestSetup(null);
|
||||
ushort basePower = 70;
|
||||
|
||||
// Act
|
||||
facade.ChangeBasePower(move, target, 0, ref basePower);
|
||||
|
||||
// Assert
|
||||
await Assert.That(basePower).IsEqualTo((ushort)70);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: the base power only doubles "if the user is poisoned, paralyzed, or burned" — freeze is
|
||||
/// not one of the boosting statuses.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_UserFrozen_BasePowerUnchanged()
|
||||
{
|
||||
// Arrange
|
||||
var (facade, move, target) = CreateTestSetup(new Frozen());
|
||||
ushort basePower = 70;
|
||||
|
||||
// Act
|
||||
facade.ChangeBasePower(move, target, 0, ref basePower);
|
||||
|
||||
// Assert
|
||||
await Assert.That(basePower).IsEqualTo((ushort)70);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: the base power only doubles "if the user is poisoned, paralyzed, or burned" — sleep is
|
||||
/// not one of the boosting statuses.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_UserAsleep_BasePowerUnchanged()
|
||||
{
|
||||
// Arrange
|
||||
var (facade, move, target) = CreateTestSetup(new Sleep());
|
||||
ushort basePower = 70;
|
||||
|
||||
// Act
|
||||
facade.ChangeBasePower(move, target, 0, ref basePower);
|
||||
|
||||
// Assert
|
||||
await Assert.That(basePower).IsEqualTo((ushort)70);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Technical test: doubling a very large base power clamps at <see cref="ushort.MaxValue"/> instead of
|
||||
/// overflowing.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeBasePower_UserBurnedWithLargeBasePower_ClampsAtMaxValue()
|
||||
{
|
||||
// Arrange
|
||||
var (facade, move, target) = CreateTestSetup(new Burned());
|
||||
ushort basePower = ushort.MaxValue - 100;
|
||||
|
||||
// Act
|
||||
facade.ChangeBasePower(move, target, 0, ref basePower);
|
||||
|
||||
// Assert
|
||||
await Assert.That(basePower).IsEqualTo(ushort.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia (Generation VI onwards): "When using Facade, Burn's effect of halving the damage done by
|
||||
/// physical moves is now ignored."
|
||||
///
|
||||
/// <see cref="Facade"/> implements this by pre-doubling the damage so that the halving <see cref="Burned"/>
|
||||
/// applies afterwards cancels out. Both hooks are registered on the executing move, and
|
||||
/// <c>ExecutingMoveImpl.CollectScripts</c> yields the move script before the user's status script, so this
|
||||
/// drives them in that order.
|
||||
///
|
||||
/// This test only pins the composition; that each half individually does its part is covered by
|
||||
/// <see cref="ChangeMoveDamage_BurnedUser_DamageDoubledToOffsetBurn"/> and
|
||||
/// <see cref="ChangeMoveDamage_BurnedUserPhysicalMoveWithoutFacade_DamageHalved"/>.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeMoveDamage_BurnedUserUsingFacade_BurnHalvingIgnored()
|
||||
{
|
||||
// Arrange
|
||||
var burned = new Burned();
|
||||
var (facade, move, target) = CreateTestSetup(burned);
|
||||
uint damage = 100;
|
||||
|
||||
// Act
|
||||
facade.ChangeMoveDamage(move, target, 0, ref damage);
|
||||
burned.ChangeMoveDamage(move, target, 0, ref damage);
|
||||
|
||||
// Assert
|
||||
await Assert.That(damage).IsEqualTo(100u);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The half of the above that belongs to <see cref="Facade"/>: a burned user's physical damage is doubled,
|
||||
/// offsetting the halving <see cref="Burned"/> applies later in the hook chain.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeMoveDamage_BurnedUser_DamageDoubledToOffsetBurn()
|
||||
{
|
||||
// Arrange
|
||||
var (facade, move, target) = CreateTestSetup(new Burned());
|
||||
uint damage = 100;
|
||||
|
||||
// Act
|
||||
facade.ChangeMoveDamage(move, target, 0, ref damage);
|
||||
|
||||
// Assert
|
||||
await Assert.That(damage).IsEqualTo(200u);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The half of the above that belongs to <see cref="Burned"/>: without Facade's compensating hook, a burned
|
||||
/// user's physical damage is halved. Bulbapedia: burn "halves damage dealt by a burned Pokémon's physical
|
||||
/// moves".
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeMoveDamage_BurnedUserPhysicalMoveWithoutFacade_DamageHalved()
|
||||
{
|
||||
// Arrange
|
||||
var burned = new Burned();
|
||||
var (_, move, target) = CreateTestSetup(burned);
|
||||
uint damage = 100;
|
||||
|
||||
// Act
|
||||
burned.ChangeMoveDamage(move, target, 0, ref damage);
|
||||
|
||||
// Assert
|
||||
await Assert.That(damage).IsEqualTo(50u);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Burn is the only status whose damage penalty Facade offsets — a paralyzed user gets the doubled base
|
||||
/// power but no damage compensation, since paralysis does not reduce damage.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeMoveDamage_ParalyzedUser_DamageUnchanged()
|
||||
{
|
||||
// Arrange
|
||||
var (facade, move, target) = CreateTestSetup(new Paralyzed());
|
||||
uint damage = 100;
|
||||
|
||||
// Act
|
||||
facade.ChangeMoveDamage(move, target, 0, ref damage);
|
||||
|
||||
// Assert
|
||||
await Assert.That(damage).IsEqualTo(100u);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Burn only halves physical damage, so Facade must not compensate for a special move. Facade itself is
|
||||
/// physical; this pins the category guard rather than an in-game scenario.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task ChangeMoveDamage_BurnedUserSpecialMove_DamageUnchanged()
|
||||
{
|
||||
// Arrange
|
||||
var (facade, move, target) = CreateTestSetup(new Burned(), MoveCategory.Special);
|
||||
uint damage = 100;
|
||||
|
||||
// Act
|
||||
facade.ChangeMoveDamage(move, target, 0, ref damage);
|
||||
|
||||
// Assert
|
||||
await Assert.That(damage).IsEqualTo(100u);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user