mirror of
https://github.com/correl/mage.git
synced 2024-12-24 11:50:45 +00:00
* Genesis Ultimatum - fixed rollback error on usage with modal double faces cards (#7275);
This commit is contained in:
parent
796c8fb22e
commit
10cf9c4a4e
9 changed files with 94 additions and 46 deletions
|
@ -1,6 +1,5 @@
|
|||
package mage.cards.g;
|
||||
|
||||
import mage.MageItem;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileSpellEffect;
|
||||
|
@ -18,7 +17,6 @@ import mage.players.Player;
|
|||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
@ -49,7 +47,7 @@ class GenesisUltimatumEffect extends OneShotEffect {
|
|||
private static final FilterCard filter = new FilterPermanentCard("any number of permanent cards");
|
||||
|
||||
GenesisUltimatumEffect() {
|
||||
super(Outcome.Benefit);
|
||||
super(Outcome.PutCardInPlay);
|
||||
staticText = "Look at the top five cards of your library. Put any number of permanent cards " +
|
||||
"from among them onto the battlefield and the rest into your hand";
|
||||
}
|
||||
|
@ -72,14 +70,13 @@ class GenesisUltimatumEffect extends OneShotEffect {
|
|||
Cards toHand = new CardsImpl(player.getLibrary().getTopCards(game, 5));
|
||||
player.lookAtCards(source, null, toHand, game);
|
||||
TargetCard targetCard = new TargetCardInLibrary(0, 5, filter);
|
||||
targetCard.withChooseHint("put to battlefield");
|
||||
player.choose(outcome, toHand, targetCard, game);
|
||||
Cards toBattlefield = new CardsImpl(targetCard.getTargets());
|
||||
if (player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game)) {
|
||||
toBattlefield
|
||||
.stream()
|
||||
.map(game::getPermanent)
|
||||
.filter(Objects::nonNull) // to prevent exception https://github.com/magefree/mage/issues/7220
|
||||
.map(MageItem::getId)
|
||||
.filter(id -> Zone.BATTLEFIELD.equals(game.getState().getZone(id)))
|
||||
.forEach(toHand::remove);
|
||||
}
|
||||
player.moveCards(toHand, Zone.HAND, source, game);
|
||||
|
|
|
@ -61,7 +61,7 @@ class SelectiveAdaptationEffect extends OneShotEffect {
|
|||
private final String abilityName;
|
||||
private final FilterCard filter;
|
||||
|
||||
private AbilitySelector(Class abilityClass, String abilityName) {
|
||||
AbilitySelector(Class abilityClass, String abilityName) {
|
||||
this.abilityClass = abilityClass;
|
||||
this.abilityName = abilityName;
|
||||
this.filter = new FilterCard("card with " + abilityName);
|
||||
|
@ -121,7 +121,7 @@ class SelectiveAdaptationEffect extends OneShotEffect {
|
|||
Card toBattlefield = game.getCard(target.getFirstTarget());
|
||||
if (toBattlefield != null
|
||||
&& player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game)
|
||||
&& game.getPermanent(toBattlefield.getId()) != null) {
|
||||
&& Zone.BATTLEFIELD.equals(game.getState().getZone(toBattlefield.getId()))) {
|
||||
toHand.remove(toBattlefield);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
package org.mage.test.cards.single.iko;
|
||||
|
||||
public class BlitzLeechTest {
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package org.mage.test.cards.single.iko;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
|
||||
public class GenesisUltimatumTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void test_Playable() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
removeAllCardsFromHand(playerA);
|
||||
|
||||
// Look at the top five cards of your library. Put any number of permanent cards from among them onto
|
||||
// the battlefield and the rest into your hand. Exile Genesis Ultimatum.
|
||||
addCard(Zone.HAND, playerA, "Genesis Ultimatum"); // {G}{G}{U}{U}{U}{R}{R}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
//
|
||||
addCard(Zone.LIBRARY, playerA, "Grizzly Bears", 1);
|
||||
addCard(Zone.LIBRARY, playerA, "Alpha Tyrranax", 1);
|
||||
addCard(Zone.LIBRARY, playerA, "Kitesail Corsair", 1);
|
||||
addCard(Zone.LIBRARY, playerA, "Riverglide Pathway", 1); // mdf card
|
||||
|
||||
// cast spell and put 3 cards to battle
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Genesis Ultimatum");
|
||||
setChoice(playerA, "Grizzly Bears^Kitesail Corsair^Riverglide Pathway");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertExileCount(playerA, "Genesis Ultimatum", 1);
|
||||
assertPermanentCount(playerA, "Grizzly Bears", 1);
|
||||
assertPermanentCount(playerA, "Kitesail Corsair", 1);
|
||||
assertPermanentCount(playerA, "Riverglide Pathway", 1);
|
||||
assertHandCount(playerA, "Alpha Tyrranax", 1);
|
||||
assertLibraryCount(playerA, 0);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,3 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.mage.test.cards.single.iko;
|
||||
|
||||
|
||||
|
|
|
@ -90,6 +90,14 @@ public interface Game extends MageItem, Serializable {
|
|||
|
||||
Spell getSpellOrLKIStack(UUID spellId);
|
||||
|
||||
/**
|
||||
* Find permanent on the battlefield by id. If you works with cards and want to check it on battlefield then
|
||||
* use game.getState().getZone() instead. Card's id and permanent's id can be different (example: mdf card
|
||||
* puts half card to battlefield, not the main card).
|
||||
*
|
||||
* @param permanentId
|
||||
* @return
|
||||
*/
|
||||
Permanent getPermanent(UUID permanentId);
|
||||
|
||||
Permanent getPermanentOrLKIBattlefield(UUID permanentId);
|
||||
|
|
|
@ -342,7 +342,6 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
MageObject object;
|
||||
if (state.getBattlefield().containsPermanent(objectId)) {
|
||||
object = state.getBattlefield().getPermanent(objectId);
|
||||
// state.setZone(objectId, Zone.BATTLEFIELD); // why is this neccessary?
|
||||
return object;
|
||||
}
|
||||
if (getPermanentsEntering().containsKey(objectId)) {
|
||||
|
@ -350,7 +349,6 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
}
|
||||
for (StackObject item : state.getStack()) {
|
||||
if (item.getId().equals(objectId)) {
|
||||
// state.setZone(objectId, Zone.STACK); // why is this neccessary?
|
||||
return item;
|
||||
}
|
||||
if (item.getSourceId().equals(objectId) && item instanceof Spell) {
|
||||
|
|
|
@ -167,8 +167,8 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
this.values.put(entry.getKey(), ((HashSet) entry.getValue()).clone());
|
||||
} else if (entry.getValue() instanceof EnumSet) {
|
||||
this.values.put(entry.getKey(), ((EnumSet) entry.getValue()).clone());
|
||||
} else if (entry.getValue() instanceof HashMap){
|
||||
this.values.put(entry.getKey(), ((HashMap) entry.getValue()).clone());
|
||||
} else if (entry.getValue() instanceof HashMap) {
|
||||
this.values.put(entry.getKey(), ((HashMap) entry.getValue()).clone());
|
||||
} else {
|
||||
this.values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
@ -696,9 +696,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
|
||||
public Permanent getPermanent(UUID permanentId) {
|
||||
if (permanentId != null && battlefield.containsPermanent(permanentId)) {
|
||||
Permanent permanent = battlefield.getPermanent(permanentId);
|
||||
// setZone(permanent.getId(), Zone.BATTLEFIELD); // shouldn't this be set anyway? (LevelX2)
|
||||
return permanent;
|
||||
return battlefield.getPermanent(permanentId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
package mage.game.permanent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.abilities.keyword.PhasingAbility;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
|
@ -57,8 +58,8 @@ public class Battlefield implements Serializable {
|
|||
return (int) field.values()
|
||||
.stream()
|
||||
.filter(permanent -> permanent.isControlledBy(controllerId)
|
||||
&& filter.match(permanent, game)
|
||||
&& permanent.isPhasedIn())
|
||||
&& filter.match(permanent, game)
|
||||
&& permanent.isPhasedIn())
|
||||
.count();
|
||||
|
||||
}
|
||||
|
@ -68,8 +69,8 @@ public class Battlefield implements Serializable {
|
|||
* influence of the specified player id and that match the supplied filter.
|
||||
*
|
||||
* @param filter
|
||||
* @param sourceId - sourceId of the MageObject the calling effect/ability
|
||||
* belongs to
|
||||
* @param sourceId - sourceId of the MageObject the calling effect/ability
|
||||
* belongs to
|
||||
* @param sourcePlayerId
|
||||
* @param game
|
||||
* @return count
|
||||
|
@ -79,15 +80,15 @@ public class Battlefield implements Serializable {
|
|||
return (int) field.values()
|
||||
.stream()
|
||||
.filter(permanent -> filter.match(permanent, sourceId, sourcePlayerId, game)
|
||||
&& permanent.isPhasedIn())
|
||||
&& permanent.isPhasedIn())
|
||||
.count();
|
||||
} else {
|
||||
List<UUID> range = game.getState().getPlayersInRange(sourcePlayerId, game);
|
||||
return (int) field.values()
|
||||
.stream()
|
||||
.filter(permanent -> range.contains(permanent.getControllerId())
|
||||
&& filter.match(permanent, sourceId, sourcePlayerId, game)
|
||||
&& permanent.isPhasedIn()).count();
|
||||
&& filter.match(permanent, sourceId, sourcePlayerId, game)
|
||||
&& permanent.isPhasedIn()).count();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +105,7 @@ public class Battlefield implements Serializable {
|
|||
return field.values()
|
||||
.stream()
|
||||
.filter(permanent -> filter.match(permanent, game)
|
||||
&& permanent.isPhasedIn()).count() >= num;
|
||||
&& permanent.isPhasedIn()).count() >= num;
|
||||
|
||||
}
|
||||
|
||||
|
@ -123,8 +124,8 @@ public class Battlefield implements Serializable {
|
|||
return field.values()
|
||||
.stream()
|
||||
.filter(permanent -> permanent.isControlledBy(controllerId)
|
||||
&& filter.match(permanent, game)
|
||||
&& permanent.isPhasedIn())
|
||||
&& filter.match(permanent, game)
|
||||
&& permanent.isPhasedIn())
|
||||
.count() >= num;
|
||||
|
||||
}
|
||||
|
@ -144,14 +145,14 @@ public class Battlefield implements Serializable {
|
|||
if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) {
|
||||
return field.values().stream()
|
||||
.filter(permanent -> filter.match(permanent, null, sourcePlayerId, game)
|
||||
&& permanent.isPhasedIn()).count() >= num;
|
||||
&& permanent.isPhasedIn()).count() >= num;
|
||||
|
||||
} else {
|
||||
List<UUID> range = game.getState().getPlayersInRange(sourcePlayerId, game);
|
||||
return field.values().stream()
|
||||
.filter(permanent -> range.contains(permanent.getControllerId())
|
||||
&& filter.match(permanent, null, sourcePlayerId, game)
|
||||
&& permanent.isPhasedIn())
|
||||
&& filter.match(permanent, null, sourcePlayerId, game)
|
||||
&& permanent.isPhasedIn())
|
||||
.count() >= num;
|
||||
}
|
||||
}
|
||||
|
@ -168,6 +169,14 @@ public class Battlefield implements Serializable {
|
|||
field.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find permanent on the battlefield by id. If you works with cards and want to check it on battlefield then
|
||||
* use game.getState().getZone() instead. Card's id and permanent's id can be different (example: mdf card
|
||||
* puts half card to battlefield, not the main card).
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public boolean containsPermanent(UUID key) {
|
||||
return field.containsKey(key);
|
||||
}
|
||||
|
@ -211,7 +220,7 @@ public class Battlefield implements Serializable {
|
|||
return field.values()
|
||||
.stream()
|
||||
.filter(perm -> perm.isPhasedIn()
|
||||
&& perm.isControlledBy(controllerId))
|
||||
&& perm.isControlledBy(controllerId))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
@ -300,7 +309,7 @@ public class Battlefield implements Serializable {
|
|||
return field.values()
|
||||
.stream()
|
||||
.filter(perm -> perm.isPhasedIn() && range.contains(perm.getControllerId())
|
||||
&& filter.match(perm, sourceId, sourcePlayerId, game)).collect(Collectors.toList());
|
||||
&& filter.match(perm, sourceId, sourcePlayerId, game)).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,7 +330,7 @@ public class Battlefield implements Serializable {
|
|||
return field.values()
|
||||
.stream()
|
||||
.filter(perm -> perm.isPhasedIn()
|
||||
&& range.contains(perm.getControllerId()))
|
||||
&& range.contains(perm.getControllerId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
@ -331,8 +340,8 @@ public class Battlefield implements Serializable {
|
|||
return field.values()
|
||||
.stream()
|
||||
.filter(perm -> perm.hasAbility(PhasingAbility.getInstance(), game)
|
||||
&& perm.isPhasedIn()
|
||||
&& perm.isControlledBy(controllerId))
|
||||
&& perm.isPhasedIn()
|
||||
&& perm.isControlledBy(controllerId))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue