mirror of
https://github.com/correl/mage.git
synced 2024-12-24 11:50:45 +00:00
* Fixed spell's target still legal handling check as it begins to resolve (fixes problem of Sublime Epiphany #6646).
This commit is contained in:
parent
ae165e5197
commit
41abefa8e4
2 changed files with 112 additions and 24 deletions
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.modal;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class OneOrMoreTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* Sublime Epiphany can bounce and
|
||||
* copy the same creature. This is because legality of targets is checked
|
||||
* only as the spell begins to resolve, not in between modes, and because
|
||||
* the games can use last known info of the legal target.
|
||||
*/
|
||||
@Test
|
||||
public void testSubtleStrikeFirstMode() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
|
||||
// Choose one or more —
|
||||
// 1 • Counter target spell
|
||||
// 2 • Counter target activated or triggered ability.
|
||||
// 3 • Return target nonland permanent to its owner's hand.
|
||||
// 4 • Create a token that's a copy of target creature you control.
|
||||
// 5 • Target player draws a card.
|
||||
addCard(Zone.HAND, playerA, "Sublime Epiphany"); // Instant {4}{U}{U}
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sublime Epiphany", "Silvercoat Lion");
|
||||
setModeChoice(playerA, "3");
|
||||
setModeChoice(playerA, "4");
|
||||
addTarget(playerA, "Silvercoat Lion");
|
||||
setModeChoice(playerA, "5");
|
||||
addTarget(playerA, playerB);
|
||||
setModeChoice(playerA, null);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertHandCount(playerB, 1);
|
||||
assertHandCount(playerA, "Silvercoat Lion", 1);
|
||||
|
||||
assertPowerToughness(playerA, "Silvercoat Lion", 2, 2);
|
||||
|
||||
Permanent perm = getPermanent("Silvercoat Lion");
|
||||
Assert.assertTrue("Silvercoat Lion has to be a Token", perm instanceof PermanentToken);
|
||||
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import mage.util.GameLog;
|
|||
import mage.util.SubTypeList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
@ -219,15 +220,14 @@ public class Spell extends StackObjImpl implements Card {
|
|||
// resolve if legal parts
|
||||
if (notTargeted || legalParts) {
|
||||
for (SpellAbility spellAbility : this.spellAbilities) {
|
||||
if (spellAbilityHasLegalParts(spellAbility, game)) {
|
||||
// legality of targets is checked only as the spell begins to resolve, not in between modes (spliced spells handeled correctly?)
|
||||
if (spellAbilityCheckTargetsAndDeactivateModes(spellAbility, game)) {
|
||||
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
|
||||
spellAbility.getModes().setActiveMode(modeId);
|
||||
if (spellAbility.getTargets().stillLegal(spellAbility, game)) {
|
||||
if (spellAbility.getSpellAbilityType() != SpellAbilityType.SPLICE) {
|
||||
updateOptionalCosts(index);
|
||||
}
|
||||
result |= spellAbility.resolve(game);
|
||||
if (spellAbility.getSpellAbilityType() != SpellAbilityType.SPLICE) {
|
||||
updateOptionalCosts(index);
|
||||
}
|
||||
result |= spellAbility.resolve(game);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
@ -318,6 +318,31 @@ public class Spell extends StackObjImpl implements Card {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Legality of the targets of all modes are only checked as the spell begins to resolve
|
||||
* A mode without any legal target (if it has targets at all) won't resolve.
|
||||
* So modes with targets without legal targets are unselected.
|
||||
*
|
||||
* @param spellAbility
|
||||
* @param game
|
||||
* @return
|
||||
*/
|
||||
private boolean spellAbilityCheckTargetsAndDeactivateModes(SpellAbility spellAbility, Game game) {
|
||||
boolean legalModes = false;
|
||||
for (Iterator<UUID> iterator = spellAbility.getModes().getSelectedModes().iterator(); iterator.hasNext();) {
|
||||
UUID nextSelectedModeId = iterator.next();
|
||||
Mode mode = spellAbility.getModes().get(nextSelectedModeId);
|
||||
if (!mode.getTargets().isEmpty()) {
|
||||
if (!mode.getTargets().stillLegal(spellAbility, game)) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
legalModes = true;
|
||||
}
|
||||
return legalModes;
|
||||
}
|
||||
|
||||
private boolean spellAbilityHasLegalParts(SpellAbility spellAbility, Game game) {
|
||||
if (spellAbility.getModes().getSelectedModes().size() > 1) {
|
||||
boolean targetedMode = false;
|
||||
|
@ -606,7 +631,9 @@ public class Spell extends StackObjImpl implements Card {
|
|||
spellAbilities.add(spellAbility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAbility(Ability ability) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -709,7 +736,7 @@ public class Spell extends StackObjImpl implements Card {
|
|||
}
|
||||
|
||||
public Spell copySpell(UUID newController) {
|
||||
Spell copy = new Spell(this.card, this.ability.copySpell(), this.controllerId, this.fromZone);
|
||||
Spell spellCopy = new Spell(this.card, this.ability.copySpell(), this.controllerId, this.fromZone);
|
||||
boolean firstDone = false;
|
||||
for (SpellAbility spellAbility : this.getSpellAbilities()) {
|
||||
if (!firstDone) {
|
||||
|
@ -718,11 +745,11 @@ public class Spell extends StackObjImpl implements Card {
|
|||
}
|
||||
SpellAbility newAbility = spellAbility.copy(); // e.g. spliced spell
|
||||
newAbility.newId();
|
||||
copy.addSpellAbility(newAbility);
|
||||
spellCopy.addSpellAbility(newAbility);
|
||||
}
|
||||
copy.setCopy(true, this);
|
||||
copy.setControllerId(newController);
|
||||
return copy;
|
||||
spellCopy.setCopy(true, this);
|
||||
spellCopy.setControllerId(newController);
|
||||
return spellCopy;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -947,12 +974,12 @@ public class Spell extends StackObjImpl implements Card {
|
|||
}
|
||||
if (isTransformable()) {
|
||||
Card secondCard = getSecondCardFace();
|
||||
ObjectColor color = secondCard.getColor(null);
|
||||
mana.setBlack(mana.isBlack() || color.isBlack());
|
||||
mana.setGreen(mana.isGreen() || color.isGreen());
|
||||
mana.setRed(mana.isRed() || color.isRed());
|
||||
mana.setBlue(mana.isBlue() || color.isBlue());
|
||||
mana.setWhite(mana.isWhite() || color.isWhite());
|
||||
ObjectColor objectColor = secondCard.getColor(null);
|
||||
mana.setBlack(mana.isBlack() || objectColor.isBlack());
|
||||
mana.setGreen(mana.isGreen() || objectColor.isGreen());
|
||||
mana.setRed(mana.isRed() || objectColor.isRed());
|
||||
mana.setBlue(mana.isBlue() || objectColor.isBlue());
|
||||
mana.setWhite(mana.isWhite() || objectColor.isWhite());
|
||||
for (String rule : secondCard.getRules()) {
|
||||
rule = rule.replaceAll("(?i)<i.*?</i>", ""); // Ignoring reminder text in italic
|
||||
if (!mana.isBlack() && rule.matches(regexBlack)) {
|
||||
|
@ -1006,21 +1033,21 @@ public class Spell extends StackObjImpl implements Card {
|
|||
|
||||
@Override
|
||||
public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount) {
|
||||
Spell copy = null;
|
||||
Spell spellCopy = null;
|
||||
GameEvent gameEvent = GameEvent.getEvent(EventType.COPY_STACKOBJECT, this.getId(), source.getSourceId(), newControllerId, amount);
|
||||
if (game.replaceEvent(gameEvent)) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < gameEvent.getAmount(); i++) {
|
||||
copy = this.copySpell(newControllerId);
|
||||
game.getState().setZone(copy.getId(), Zone.STACK); // required for targeting ex: Nivmagus Elemental
|
||||
game.getStack().push(copy);
|
||||
spellCopy = this.copySpell(newControllerId);
|
||||
game.getState().setZone(spellCopy.getId(), Zone.STACK); // required for targeting ex: Nivmagus Elemental
|
||||
game.getStack().push(spellCopy);
|
||||
if (chooseNewTargets) {
|
||||
copy.chooseNewTargets(game, newControllerId);
|
||||
spellCopy.chooseNewTargets(game, newControllerId);
|
||||
}
|
||||
game.fireEvent(new GameEvent(EventType.COPIED_STACKOBJECT, copy.getId(), this.getId(), newControllerId));
|
||||
game.fireEvent(new GameEvent(EventType.COPIED_STACKOBJECT, spellCopy.getId(), this.getId(), newControllerId));
|
||||
}
|
||||
return copy;
|
||||
return spellCopy;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in a new issue