mirror of
https://github.com/correl/mage.git
synced 2024-11-15 03:00:16 +00:00
* Copy activated ability - fixed that it raise error on triggering on copied permanents;
This commit is contained in:
parent
23ef0e4269
commit
ff1299cca9
2 changed files with 107 additions and 11 deletions
|
@ -0,0 +1,87 @@
|
|||
package org.mage.test.cards.triggers.damage;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class SyrCarahTheBoldTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void test_Damage() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
removeAllCardsFromHand(playerA);
|
||||
// When Syr Carah, the Bold or an instant or sorcery spell you control deals damage to a player, exile the top card of your library. You may play that card this turn.
|
||||
// {T}: Syr Carah deals 1 damage to any target.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Syr Carah, the Bold", 1);
|
||||
//
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
//
|
||||
addCard(Zone.LIBRARY, playerA, "Swamp", 5);
|
||||
//
|
||||
// {1}, {T}, Sacrifice Aeolipile: It deals 2 damage to any target.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Aeolipile", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
|
||||
|
||||
// 1 - triggers on ability damage
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", playerB);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkLife("damage 1", 1, PhaseStep.PRECOMBAT_MAIN, playerB, 20 - 1);
|
||||
|
||||
// 2 - triggers on spell damage
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
|
||||
waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
checkLife("damage 2", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, 20 - 1 - 3);
|
||||
|
||||
// 3 - NONE triggers on another ability damage
|
||||
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}, {T}, Sacrifice", playerA);
|
||||
checkLife("damage 3", 2, PhaseStep.BEGIN_COMBAT, playerA, 20 - 2);
|
||||
|
||||
// 4 - triggers on combat damage
|
||||
attack(3, playerA, "Syr Carah, the Bold", playerB);
|
||||
checkLife("damage 4", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, 20 - 1 - 3 - 3);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DamageWithCopyAbility() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
removeAllCardsFromHand(playerA);
|
||||
// When Syr Carah, the Bold or an instant or sorcery spell you control deals damage to a player, exile the top card of your library. You may play that card this turn.
|
||||
// {T}: Syr Carah deals 1 damage to any target.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Syr Carah, the Bold", 1);
|
||||
//
|
||||
addCard(Zone.LIBRARY, playerA, "Swamp", 5);
|
||||
//
|
||||
// {T}: Embermage Goblin deals 1 damage to any target.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Embermage Goblin", 1);
|
||||
//
|
||||
// Whenever an ability of equipped creature is activated, if it isn't a mana ability, copy that ability. You may choose new targets for the copy.
|
||||
// Equip 3
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Illusionist's Bracers", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Island", 3);
|
||||
|
||||
// equip to copy abilities
|
||||
showAvaileableAbilities("abils", 2, PhaseStep.PRECOMBAT_MAIN, playerB);
|
||||
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Equip {3}", "Embermage Goblin");
|
||||
setChoice(playerB, "No"); // no new target
|
||||
|
||||
// 3 - 2x damage (copy), but no trigger
|
||||
// java.lang.ClassCastException: mage.game.stack.StackAbility cannot be cast to mage.game.stack.Spell
|
||||
activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{T}: {source} deals", playerA);
|
||||
checkLife("damage 3", 2, PhaseStep.END_TURN, playerA, 20 - 1 - 1);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ import mage.filter.Filter;
|
|||
import mage.filter.FilterCard;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.filter.predicate.mageobject.SupertypePredicate;
|
||||
import mage.filter.predicate.permanent.ControllerIdPredicate;
|
||||
|
@ -45,7 +46,6 @@ import mage.game.permanent.Permanent;
|
|||
import mage.game.permanent.PermanentCard;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.SpellStack;
|
||||
import mage.game.stack.StackAbility;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.game.turn.Phase;
|
||||
import mage.game.turn.Step;
|
||||
|
@ -69,7 +69,6 @@ import java.io.IOException;
|
|||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
|
||||
public abstract class GameImpl implements Game, Serializable {
|
||||
|
||||
|
@ -452,7 +451,17 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
public Spell getSpellOrLKIStack(UUID spellId) {
|
||||
Spell spell = state.getStack().getSpell(spellId);
|
||||
if (spell == null) {
|
||||
spell = (Spell) this.getLastKnownInformation(spellId, Zone.STACK);
|
||||
MageObject obj = this.getLastKnownInformation(spellId, Zone.STACK);
|
||||
if (obj instanceof Spell) {
|
||||
spell = (Spell) obj;
|
||||
} else {
|
||||
if (obj != null) {
|
||||
// copied activated abilities is StackAbility (not spell) and must be ignored here
|
||||
// if not then java.lang.ClassCastException: mage.game.stack.StackAbility cannot be cast to mage.game.stack.Spell
|
||||
// see SyrCarahTheBoldTest as example
|
||||
// logger.error("getSpellOrLKIStack got non spell id - " + obj.getClass().getName() + " - " + obj.getName(), new Throwable());
|
||||
}
|
||||
}
|
||||
}
|
||||
return spell;
|
||||
}
|
||||
|
@ -1471,7 +1480,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
/**
|
||||
* @param emblem
|
||||
* @param sourceObject
|
||||
* @param toPlayerId controller and owner of the emblem
|
||||
* @param toPlayerId controller and owner of the emblem
|
||||
*/
|
||||
@Override
|
||||
public void addEmblem(Emblem emblem, MageObject sourceObject, UUID toPlayerId) {
|
||||
|
@ -1489,8 +1498,8 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
/**
|
||||
* @param plane
|
||||
* @param sourceObject
|
||||
* @param toPlayerId controller and owner of the plane (may only be one per
|
||||
* game..)
|
||||
* @param toPlayerId controller and owner of the plane (may only be one per
|
||||
* game..)
|
||||
* @return boolean - whether the plane was added successfully or not
|
||||
*/
|
||||
@Override
|
||||
|
@ -1719,7 +1728,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
break;
|
||||
}
|
||||
// triggered abilities that don't use the stack have to be executed first (e.g. Banisher Priest Return exiled creature
|
||||
for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext();) {
|
||||
for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext(); ) {
|
||||
TriggeredAbility triggeredAbility = it.next();
|
||||
if (!triggeredAbility.isUsesStack()) {
|
||||
state.removeTriggeredAbility(triggeredAbility);
|
||||
|
@ -2454,7 +2463,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
}
|
||||
//20100423 - 800.4a
|
||||
Set<Card> toOutside = new HashSet<>();
|
||||
for (Iterator<Permanent> it = getBattlefield().getAllPermanents().iterator(); it.hasNext();) {
|
||||
for (Iterator<Permanent> it = getBattlefield().getAllPermanents().iterator(); it.hasNext(); ) {
|
||||
Permanent perm = it.next();
|
||||
if (perm.isOwnedBy(playerId)) {
|
||||
if (perm.getAttachedTo() != null) {
|
||||
|
@ -2497,7 +2506,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
player.moveCards(toOutside, Zone.OUTSIDE, null, this);
|
||||
// triggered abilities that don't use the stack have to be executed
|
||||
List<TriggeredAbility> abilities = state.getTriggered(player.getId());
|
||||
for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext();) {
|
||||
for (Iterator<TriggeredAbility> it = abilities.iterator(); it.hasNext(); ) {
|
||||
TriggeredAbility triggeredAbility = it.next();
|
||||
if (!triggeredAbility.isUsesStack()) {
|
||||
state.removeTriggeredAbility(triggeredAbility);
|
||||
|
@ -2517,7 +2526,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
|
||||
// Remove cards from the player in all exile zones
|
||||
for (ExileZone exile : this.getExile().getExileZones()) {
|
||||
for (Iterator<UUID> it = exile.iterator(); it.hasNext();) {
|
||||
for (Iterator<UUID> it = exile.iterator(); it.hasNext(); ) {
|
||||
Card card = this.getCard(it.next());
|
||||
if (card != null && card.isOwnedBy(playerId)) {
|
||||
it.remove();
|
||||
|
@ -2527,7 +2536,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
|
||||
//Remove all commander/emblems/plane the player controls
|
||||
boolean addPlaneAgain = false;
|
||||
for (Iterator<CommandObject> it = this.getState().getCommand().iterator(); it.hasNext();) {
|
||||
for (Iterator<CommandObject> it = this.getState().getCommand().iterator(); it.hasNext(); ) {
|
||||
CommandObject obj = it.next();
|
||||
if (obj.isControlledBy(playerId)) {
|
||||
if (obj instanceof Emblem) {
|
||||
|
|
Loading…
Reference in a new issue