Skip to content

Commit 5e0c09b

Browse files
liamiak1claude
andcommitted
Add regression tests for casting on someone else's grant
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent fc114ed commit 5e0c09b

1 file changed

Lines changed: 112 additions & 0 deletions

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package forge.game.spellability;
2+
3+
import org.testng.annotations.Test;
4+
5+
import forge.ai.AITest;
6+
import forge.game.Game;
7+
import forge.game.ability.AbilityFactory;
8+
import forge.game.ability.AbilityUtils;
9+
import forge.game.card.Card;
10+
import forge.game.card.CounterEnumType;
11+
import forge.game.phase.PhaseType;
12+
import forge.game.player.Player;
13+
import forge.game.zone.ZoneType;
14+
15+
import static junit.framework.Assert.assertEquals;
16+
17+
/**
18+
* A grant clears the restriction zone, so it is the grant that has to reach another player's
19+
* zone. MayPlayDontGrantZonePermissions marks a cost reduction rather than permission, so on its
20+
* own it is not enough - but it must not stand in the way when a second grant does allow it.
21+
*/
22+
public class GrantedCastTest extends AITest {
23+
24+
private Game game;
25+
private Player me;
26+
private Player opp;
27+
28+
@Test
29+
public void aCostReductionAloneDoesNotReachAnotherHand() {
30+
newGame();
31+
Card af = addCard("As Foretold", me);
32+
af.addCounterInternal(CounterEnumType.TIME, 5, me, false, new forge.game.GameEntityCounterTable(), null);
33+
addCards("Island", 8, me);
34+
settle();
35+
36+
Card mine = addCardToZone("Lightning Bolt", me, ZoneType.Hand);
37+
Card theirs = addCardToZone("Lightning Bolt", opp, ZoneType.Hand);
38+
settle();
39+
40+
assertEquals("As Foretold still discounts my own card", 2, playable(mine));
41+
assertEquals("but it must not reach an opponent's hand", 0, playable(theirs));
42+
}
43+
44+
@Test
45+
public void aGrantThatReachesTheZoneStillWorks() {
46+
newGame();
47+
Card prize = addCardToZone("Lightning Bolt", opp, ZoneType.Graveyard);
48+
addCards("Island", 10, me);
49+
settle();
50+
51+
resolve(addCardToZone("Mnemonic Betrayal", me, ZoneType.Hand).getFirstSpellAbility(), null);
52+
assertEquals("Mnemonic Betrayal still casts from their graveyard", 1,
53+
playable(game.getCardState(prize, prize)));
54+
}
55+
56+
@Test
57+
public void aCostReductionRidesOnTopOfAGrant() {
58+
newGame();
59+
Card grenzo = addCard("Grenzo, Crooked Jailer", me);
60+
addCards("Mountain", 8, me);
61+
for (int i = 0; i < 6; i++) {
62+
addCardToZone("Lightning Bolt", opp, ZoneType.Library);
63+
}
64+
settle();
65+
66+
// Heist is what grants the zone; Grenzo's {0} only discounts what it finds there
67+
resolve(AbilityFactory.getAbility("DB$ Heist | ValidTgts$ Opponent", grenzo), opp);
68+
Card heisted = opp.getCardsIn(ZoneType.Exile).getFirst();
69+
assertEquals("Grenzo's discount survives alongside Heist's grant", 2, playable(heisted));
70+
}
71+
72+
@Test
73+
public void aCostReductionGrantedToTheActivePlayerReachesOnlyTheirOwnCards() {
74+
newGame();
75+
// the enchantment is theirs, but it grants to whoever is active - that is me
76+
addCard("Weftwalking", opp);
77+
addCards("Island", 8, me);
78+
settle();
79+
80+
Card mine = addCardToZone("Lightning Bolt", me, ZoneType.Hand);
81+
Card theirs = addCardToZone("Lightning Bolt", opp, ZoneType.Hand);
82+
settle();
83+
84+
assertEquals("Weftwalking still offers me the free cast of my own card", 2, playable(mine));
85+
assertEquals("but not of a card I do not own", 0, playable(theirs));
86+
}
87+
88+
private void newGame() {
89+
game = initAndCreateGame();
90+
me = game.getPlayers().get(0);
91+
opp = game.getPlayers().get(1);
92+
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, me);
93+
}
94+
95+
private void settle() {
96+
game.getAction().checkStateEffects(true);
97+
}
98+
99+
private int playable(Card c) {
100+
return c.getAllPossibleAbilities(me, true).size();
101+
}
102+
103+
private void resolve(SpellAbility sa, Player target) {
104+
sa.setActivatingPlayer(me);
105+
sa.resetTargets();
106+
if (target != null) {
107+
sa.getTargets().add(target);
108+
}
109+
AbilityUtils.resolve(sa);
110+
settle();
111+
}
112+
}

0 commit comments

Comments
 (0)