Fixing copy and cast from exile effects (#10436)

* Added unit test for magefree/mage#10435

* Added test for potential breakage of prosper functionality

* Copies of cards are now created in the right zone

* Added PlayCardTriggeredAbility

This triggered ability checks to make sure a card was actually played (as opposed to a copy of a card).
Common abilities have been refactored to use this new ability

* Added mizzix's mastery overload test

* Fixed Mizzix's mastery overload

* Added new ability to Juju Bubble

---------

Co-authored-by: xenohedron <xenohedron@users.noreply.github.com>
This commit is contained in:
Alexander Novotny 2023-06-08 18:32:59 -07:00 committed by GitHub
parent 2f79343bc8
commit a0f8a42699
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 337 additions and 225 deletions

View file

@ -445,89 +445,94 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
public boolean removeFromZone(Game game, Zone fromZone, Ability source) {
boolean removed = false;
MageObject lkiObject = null;
switch (fromZone) {
case GRAVEYARD:
removed = game.getPlayer(ownerId).removeFromGraveyard(this, game);
break;
case HAND:
removed = game.getPlayer(ownerId).removeFromHand(this, game);
break;
case LIBRARY:
removed = game.getPlayer(ownerId).removeFromLibrary(this, game);
break;
case EXILED:
if (game.getExile().getCard(getId(), game) != null) {
removed = game.getExile().removeCard(this, game);
}
break;
case STACK:
StackObject stackObject;
if (getSpellAbility() != null) {
stackObject = game.getStack().getSpell(getSpellAbility().getId(), false);
} else {
stackObject = game.getStack().getSpell(this.getId(), false);
}
if (isCopy()) { // copied cards have no need to be removed from a previous zone
removed = true;
} else {
switch (fromZone) {
case GRAVEYARD:
removed = game.getPlayer(ownerId).removeFromGraveyard(this, game);
break;
case HAND:
removed = game.getPlayer(ownerId).removeFromHand(this, game);
break;
case LIBRARY:
removed = game.getPlayer(ownerId).removeFromLibrary(this, game);
break;
case EXILED:
if (game.getExile().getCard(getId(), game) != null) {
removed = game.getExile().removeCard(this, game);
}
break;
case STACK:
StackObject stackObject;
if (getSpellAbility() != null) {
stackObject = game.getStack().getSpell(getSpellAbility().getId(), false);
} else {
stackObject = game.getStack().getSpell(this.getId(), false);
}
// handle half of Split Cards on stack
if (stackObject == null && (this instanceof SplitCard)) {
stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId(), false);
if (stackObject == null) {
stackObject = game.getStack().getSpell(((SplitCard) this).getRightHalfCard().getId(),
false);
}
}
// handle half of Modal Double Faces Cards on stack
if (stackObject == null && (this instanceof ModalDoubleFacedCard)) {
stackObject = game.getStack().getSpell(((ModalDoubleFacedCard) this).getLeftHalfCard().getId(),
false);
if (stackObject == null) {
stackObject = game.getStack()
.getSpell(((ModalDoubleFacedCard) this).getRightHalfCard().getId(), false);
}
}
if (stackObject == null && (this instanceof AdventureCard)) {
stackObject = game.getStack().getSpell(((AdventureCard) this).getSpellCard().getId(), false);
}
// handle half of Split Cards on stack
if (stackObject == null && (this instanceof SplitCard)) {
stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId(), false);
if (stackObject == null) {
stackObject = game.getStack().getSpell(((SplitCard) this).getRightHalfCard().getId(), false);
stackObject = game.getStack().getSpell(getId(), false);
}
}
// handle half of Modal Double Faces Cards on stack
if (stackObject == null && (this instanceof ModalDoubleFacedCard)) {
stackObject = game.getStack().getSpell(((ModalDoubleFacedCard) this).getLeftHalfCard().getId(), false);
if (stackObject == null) {
stackObject = game.getStack().getSpell(((ModalDoubleFacedCard) this).getRightHalfCard().getId(), false);
if (stackObject != null) {
removed = game.getStack().remove(stackObject, game);
lkiObject = stackObject;
}
}
if (stackObject == null && (this instanceof AdventureCard)) {
stackObject = game.getStack().getSpell(((AdventureCard) this).getSpellCard().getId(), false);
}
if (stackObject == null) {
stackObject = game.getStack().getSpell(getId(), false);
}
if (stackObject != null) {
removed = game.getStack().remove(stackObject, game);
lkiObject = stackObject;
}
break;
case COMMAND:
for (CommandObject commandObject : game.getState().getCommand()) {
if (commandObject.getId().equals(objectId)) {
lkiObject = commandObject;
break;
case COMMAND:
for (CommandObject commandObject : game.getState().getCommand()) {
if (commandObject.getId().equals(objectId)) {
lkiObject = commandObject;
}
}
}
if (lkiObject != null) {
removed = game.getState().getCommand().remove(lkiObject);
}
break;
case OUTSIDE:
if (isCopy()) { // copied cards have no need to be removed from a previous zone
if (lkiObject != null) {
removed = game.getState().getCommand().remove(lkiObject);
}
break;
case OUTSIDE:
if (game.getPlayer(ownerId).getSideboard().contains(this.getId())) {
game.getPlayer(ownerId).getSideboard().remove(this.getId());
removed = true;
} else if (game.getPhase() == null) {
// E.g. Commander of commander game
removed = true;
} else {
// Unstable - Summon the Pack
removed = true;
}
break;
case BATTLEFIELD: // for sacrificing permanents or putting to library
removed = true;
} else if (game.getPlayer(ownerId).getSideboard().contains(this.getId())) {
game.getPlayer(ownerId).getSideboard().remove(this.getId());
removed = true;
} else if (game.getPhase() == null) {
// E.g. Commander of commander game
removed = true;
} else {
// Unstable - Summon the Pack
removed = true;
}
break;
case BATTLEFIELD: // for sacrificing permanents or putting to library
removed = true;
break;
default:
MageObject sourceObject = game.getObject(source);
logger.fatal("Invalid from zone [" + fromZone + "] for card [" + this.getIdName()
+ "] source [" + (sourceObject != null ? sourceObject.getName() : "null") + ']');
break;
break;
default:
MageObject sourceObject = game.getObject(source);
logger.fatal("Invalid from zone [" + fromZone + "] for card [" + this.getIdName()
+ "] source [" + (sourceObject != null ? sourceObject.getName() : "null") + ']');
break;
}
}
if (removed) {
if (fromZone != Zone.OUTSIDE) {