use of mage.game.stack.StackObject in project mage by magefree.
the class GameImpl method resolve.
// resolve top StackObject
protected void resolve() {
StackObject top = null;
try {
top = state.getStack().peek();
top.resolve(this);
resetControlAfterSpellResolve(top.getId());
} finally {
if (top != null) {
// seems partly redundant because move card from stack to grave is already done and the stack removed
state.getStack().remove(top, this);
rememberLKI(top.getSourceId(), Zone.STACK, top);
checkInfiniteLoop(top.getSourceId());
if (!getTurn().isEndTurnRequested()) {
while (state.hasSimultaneousEvents()) {
state.handleSimultaneousEvent(this);
}
}
}
}
}
use of mage.game.stack.StackObject in project mage by magefree.
the class CardImpl method removeFromZone.
@Override
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);
}
// 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 ModalDoubleFacesCard)) {
stackObject = game.getStack().getSpell(((ModalDoubleFacesCard) this).getLeftHalfCard().getId(), false);
if (stackObject == null) {
stackObject = game.getStack().getSpell(((ModalDoubleFacesCard) this).getRightHalfCard().getId(), false);
}
}
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;
}
}
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
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 // for sacrificing permanents or putting to library
BATTLEFIELD:
removed = true;
break;
default:
MageObject sourceObject = game.getObject(source.getSourceId());
logger.fatal("Invalid from zone [" + fromZone + "] for card [" + this.getIdName() + "] source [" + (sourceObject != null ? sourceObject.getName() : "null") + ']');
break;
}
if (removed) {
if (fromZone != Zone.OUTSIDE) {
game.rememberLKI(lkiObject != null ? lkiObject.getId() : objectId, fromZone, lkiObject != null ? lkiObject : this);
}
} else {
logger.warn("Couldn't find card in fromZone, card=" + getIdName() + ", fromZone=" + fromZone);
// possible reason: you to remove card from wrong zone or card already removed,
// e.g. you added copy card to wrong graveyard (see owner) or removed card from graveyard before moveToZone call
}
return removed;
}
use of mage.game.stack.StackObject in project mage by magefree.
the class TargetSpellOrPermanent method canChoose.
/**
* Checks if there are enough {@link mage.game.permanent.Permanent} or
* {@link mage.game.stack.Spell} that can be chosen. Should only be used for
* Ability targets since this checks for protection, shroud etc.
*
* @param sourceId - the target event source
* @param sourceControllerId - controller of the target event source
* @param game
* @return - true if enough valid {@link mage.game.permanent.Permanent} or
* {@link mage.game.stack.Spell} exist
*/
@Override
public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) {
int count = 0;
MageObject targetSource = game.getObject(sourceId);
for (StackObject stackObject : game.getStack()) {
Spell spell = game.getStack().getSpell(stackObject.getId());
if (spell != null && !sourceId.equals(spell.getSourceId()) && filter.match(spell, sourceId, sourceControllerId, game)) {
count++;
if (count >= this.minNumberOfTargets) {
return true;
}
}
}
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) {
if (permanent.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(permanent, sourceId, sourceControllerId, game)) {
count++;
if (count >= this.minNumberOfTargets) {
return true;
}
}
}
return false;
}
use of mage.game.stack.StackObject in project mage by magefree.
the class GameImpl method checkStateBasedActions.
/**
* 116.5. Each time a player would get priority, the game first performs all
* applicable state-based actions as a single event (see rule 704,
* “State-Based Actions”), then repeats this process until no state-based
* actions are performed. Then triggered abilities are put on the stack (see
* rule 603, “Handling Triggered Abilities”). These steps repeat in order
* until no further state-based actions are performed and no abilities
* trigger. Then the player who would have received priority does so.
*
* @return
*/
protected boolean checkStateBasedActions() {
boolean somethingHappened = false;
// 20091005 - 704.5a/704.5b/704.5c
for (Player player : state.getPlayers().values()) {
if (!player.hasLost() && ((player.getLife() <= 0 && player.canLoseByZeroOrLessLife()) || player.getLibrary().isEmptyDraw() || player.getCounters().getCount(CounterType.POISON) >= 10)) {
player.lost(this);
}
}
// If a Dungeon is on its last room and is not the source of any triggered abilities, it is removed
Set<Dungeon> dungeonsToRemove = new HashSet<>();
for (CommandObject commandObject : state.getCommand()) {
if (!(commandObject instanceof Dungeon)) {
continue;
}
Dungeon dungeon = (Dungeon) commandObject;
boolean removeDungeon = !dungeon.hasNextRoom() && this.getStack().stream().filter(DungeonRoom::isRoomTrigger).map(StackObject::getSourceId).noneMatch(dungeon.getId()::equals) && this.state.getTriggered(dungeon.getControllerId()).stream().filter(DungeonRoom::isRoomTrigger).map(Ability::getSourceId).noneMatch(dungeon.getId()::equals);
if (removeDungeon) {
dungeonsToRemove.add(dungeon);
}
}
for (Dungeon dungeon : dungeonsToRemove) {
this.removeDungeon(dungeon);
somethingHappened = true;
}
// signature spells goes to command zone all the time
for (Player player : state.getPlayers().values()) {
Set<UUID> commanderIds = getCommandersIds(player, CommanderCardType.COMMANDER_OR_OATHBREAKER, false);
if (commanderIds.isEmpty()) {
continue;
}
Set<Card> commanders = new HashSet<>();
Cards toMove = new CardsImpl();
player.getGraveyard().stream().filter(commanderIds::contains).map(this::getCard).filter(Objects::nonNull).forEach(commanders::add);
commanderIds.stream().map(uuid -> getExile().getCard(uuid, this)).filter(Objects::nonNull).forEach(commanders::add);
commanders.removeIf(card -> state.checkCommanderShouldStay(card, this));
for (Card card : commanders) {
Zone currentZone = this.getState().getZone(card.getId());
String currentZoneInfo = (currentZone == null ? "(error)" : "(" + currentZone.name() + ")");
if (player.chooseUse(Outcome.Benefit, "Move " + card.getIdName() + " to the command zone or leave it in current zone " + currentZoneInfo + "?", "You can only make this choice once per object", "Move to command", "Leave in current zone " + currentZoneInfo, null, this)) {
toMove.add(card);
} else {
state.setCommanderShouldStay(card, this);
}
}
if (toMove.isEmpty()) {
continue;
}
player.moveCards(toMove, Zone.COMMAND, null, this);
somethingHappened = true;
}
// 704.5e
// If a copy of a spell is in a zone other than the stack, it ceases to exist.
// If a copy of a card is in any zone other than the stack or the battlefield, it ceases to exist.
// (Isochron Scepter) 12/1/2004: If you don't want to cast the copy, you can choose not to; the copy ceases
// to exist the next time state-based actions are checked.
//
// Copied cards can be stored in GameState.copiedCards or in game state value (until LKI rework)
// Copied cards list contains all parts of split/adventure/mdfc
Set<Card> allCopiedCards = new HashSet<>();
allCopiedCards.addAll(this.getState().getCopiedCards());
Map<String, Object> stateSavedCopiedCards = this.getState().getValues(GameState.COPIED_CARD_KEY);
allCopiedCards.addAll(stateSavedCopiedCards.values().stream().map(object -> (Card) object).filter(Objects::nonNull).collect(Collectors.toList()));
Set<Card> copiedCardsToRemove = new HashSet<>();
for (Card copiedCard : allCopiedCards) {
// 1. Zone must be checked from main card only cause mdf parts can have different zones
// (one side on battlefield, another side on outside)
// 2. Copied card creates in OUTSIDE zone and put to stack manually in the same code,
// so no SBA calls before real zone change (you will see here only unused cards like Isochron Scepter)
// (Isochron Scepter) 12/1/2004: If you don't want to cast the copy, you can choose not to; the copy ceases
// to exist the next time state-based actions are checked.
Zone zone = state.getZone(copiedCard.getMainCard().getId());
// TODO: remember LKI of copied cards here after LKI rework
switch(zone) {
case OUTSIDE:
case BATTLEFIELD:
{
// keep in outside (it's a final zone for all copied cards)
continue;
}
case STACK:
{
// copied cards aren't moves and keeps in Stack zone after resolve,
// so it must be moved manually as SBA (see Outside zone change at the end)
MageObject object = getStack().getStackObject(copiedCard.getId());
if (object != null) {
// keep in stack until resolve
continue;
}
break;
}
case GRAVEYARD:
{
for (Player player : getPlayers().values()) {
if (player.getGraveyard().contains(copiedCard.getId())) {
player.getGraveyard().remove(copiedCard);
break;
}
}
break;
}
case HAND:
{
for (Player player : getPlayers().values()) {
if (player.getHand().contains(copiedCard.getId())) {
player.getHand().remove(copiedCard);
break;
}
}
break;
}
case LIBRARY:
{
for (Player player : getPlayers().values()) {
if (player.getLibrary().getCard(copiedCard.getId(), this) != null) {
player.getLibrary().remove(copiedCard.getId(), this);
break;
}
}
break;
}
case EXILED:
{
getExile().removeCard(copiedCard, this);
break;
}
case COMMAND:
default:
{
break;
}
}
// copied card can be removed to Outside
copiedCardsToRemove.add(copiedCard);
}
// real remove
copiedCardsToRemove.forEach(card -> {
card.setZone(Zone.OUTSIDE, this);
this.getState().getCopiedCards().remove(card);
// must keep card in game state as LKI alternative until LKI rework, so don't remove from it
// TODO: change after LKI rework
// this.getState().removeValue(GameState.COPIED_CARD_KEY + copiedCard.getId().toString());
});
List<Permanent> legendary = new ArrayList<>();
List<Permanent> worldEnchantment = new ArrayList<>();
List<FilterCreaturePermanent> usePowerInsteadOfToughnessForDamageLethalityFilters = getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters();
for (Permanent perm : getBattlefield().getAllActivePermanents()) {
if (perm.isCreature(this)) {
// 20091005 - 704.5f
if (perm.getToughness().getValue() <= 0) {
if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true;
continue;
}
} else // 20091005 - 704.5g/704.5h
{
/*
* for handling Zilortha, Strength Incarnate:
* 2020-04-17: Any time the game is checking whether damage is lethal or if a creature should be destroyed for having lethal damage marked on it, use the power of your creatures rather than their toughness to check the damage against. This includes being assigned trample damage, damage from Flame Spill, and so on.
*/
boolean usePowerInsteadOfToughnessForDamageLethality = usePowerInsteadOfToughnessForDamageLethalityFilters.stream().anyMatch(filter -> filter.match(perm, this));
int lethalDamageThreshold = usePowerInsteadOfToughnessForDamageLethality ? // Zilortha, Strength Incarnate, 2020-04-17: A creature with 0 power isn’t destroyed unless it has at least 1 damage marked on it.
Math.max(perm.getPower().getValue(), 1) : perm.getToughness().getValue();
if (lethalDamageThreshold <= perm.getDamage() || perm.isDeathtouched()) {
if (perm.destroy(null, this, false)) {
somethingHappened = true;
continue;
}
}
}
if (perm.getPairedCard() != null) {
// 702.93e.: ...another player gains control
// ...or the creature it's paired with leaves the battlefield.
Permanent paired = perm.getPairedCard().getPermanent(this);
if (paired == null || !perm.isControlledBy(paired.getControllerId()) || paired.getPairedCard() == null) {
perm.setPairedCard(null);
if (paired != null && paired.getPairedCard() != null) {
paired.setPairedCard(null);
}
somethingHappened = true;
}
}
if (perm.getBandedCards() != null && !perm.getBandedCards().isEmpty()) {
for (UUID bandedId : new ArrayList<>(perm.getBandedCards())) {
Permanent banded = getPermanent(bandedId);
if (banded == null || !perm.isControlledBy(banded.getControllerId()) || !banded.getBandedCards().contains(perm.getId())) {
perm.removeBandedCard(bandedId);
if (banded != null && banded.getBandedCards().contains(perm.getId())) {
banded.removeBandedCard(perm.getId());
}
somethingHappened = true;
}
}
}
} else if (perm.getPairedCard() != null) {
// 702.93e.: ...stops being a creature
Permanent paired = perm.getPairedCard().getPermanent(this);
perm.setPairedCard(null);
if (paired != null) {
paired.setPairedCard(null);
}
somethingHappened = true;
} else if (perm.getBandedCards() != null && !perm.getBandedCards().isEmpty()) {
perm.clearBandedCards();
for (UUID bandedId : perm.getBandedCards()) {
Permanent banded = getPermanent(bandedId);
if (banded != null) {
banded.removeBandedCard(perm.getId());
}
somethingHappened = true;
}
}
if (perm.isPlaneswalker(this)) {
// 20091005 - 704.5i
if (perm.getCounters(this).getCount(CounterType.LOYALTY) == 0) {
if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true;
continue;
}
}
}
if (perm.isWorld()) {
worldEnchantment.add(perm);
}
if (perm.hasSubtype(SubType.AURA, this)) {
// 20091005 - 704.5n, 702.14c
if (perm.getAttachedTo() == null) {
if (!perm.isCreature(this) && !perm.getAbilities(this).containsClass(BestowAbility.class)) {
if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true;
}
}
} else {
Ability spellAbility = perm.getSpellAbility();
if (spellAbility == null) {
if (!perm.getAbilities().isEmpty()) {
// Can happen for created tokens (e.g. Estrid, the Masked)
spellAbility = perm.getAbilities().get(0);
}
}
if (spellAbility.getTargets().isEmpty()) {
for (Ability ability : perm.getAbilities(this)) {
if ((ability instanceof SpellAbility) && SpellAbilityType.BASE_ALTERNATE == ((SpellAbility) ability).getSpellAbilityType() && !ability.getTargets().isEmpty()) {
spellAbility = ability;
break;
}
}
}
if (spellAbility.getTargets().isEmpty()) {
Permanent enchanted = this.getPermanent(perm.getAttachedTo());
logger.error("Aura without target: " + perm.getName() + " attached to " + (enchanted == null ? " null" : enchanted.getName()));
} else {
Target target = spellAbility.getTargets().get(0);
if (target instanceof TargetPermanent) {
Permanent attachedTo = getPermanent(perm.getAttachedTo());
if (attachedTo == null || !attachedTo.getAttachments().contains(perm.getId())) {
// handle bestow unattachment
Card card = this.getCard(perm.getId());
if (card != null && card.isCreature(this)) {
UUID wasAttachedTo = perm.getAttachedTo();
perm.attachTo(null, null, this);
fireEvent(new UnattachedEvent(wasAttachedTo, perm.getId(), perm, null));
} else if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true;
}
} else {
Filter auraFilter = spellAbility.getTargets().get(0).getFilter();
if (auraFilter instanceof FilterPermanent) {
if (!((FilterPermanent) auraFilter).match(attachedTo, perm.getId(), perm.getControllerId(), this) || attachedTo.cantBeAttachedBy(perm, null, this, true)) {
Card card = this.getCard(perm.getId());
if (card != null && card.isCreature(this)) {
UUID wasAttachedTo = perm.getAttachedTo();
perm.attachTo(null, null, this);
BestowAbility.becomeCreature(perm, this);
fireEvent(new UnattachedEvent(wasAttachedTo, perm.getId(), perm, null));
} else if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true;
}
}
} else if (!auraFilter.match(attachedTo, this) || attachedTo.cantBeAttachedBy(perm, null, this, true)) {
// handle bestow unattachment
Card card = this.getCard(perm.getId());
if (card != null && card.isCreature(this)) {
UUID wasAttachedTo = perm.getAttachedTo();
perm.attachTo(null, null, this);
BestowAbility.becomeCreature(perm, this);
fireEvent(new UnattachedEvent(wasAttachedTo, perm.getId(), perm, null));
} else if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true;
}
}
}
} else if (target instanceof TargetPlayer) {
Player attachedToPlayer = getPlayer(perm.getAttachedTo());
if (attachedToPlayer == null || attachedToPlayer.hasLost()) {
if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true;
}
} else {
Filter auraFilter = spellAbility.getTargets().get(0).getFilter();
if (!auraFilter.match(attachedToPlayer, this) || attachedToPlayer.hasProtectionFrom(perm, this)) {
if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true;
}
}
}
} else if (target instanceof TargetCard) {
Card attachedTo = getCard(perm.getAttachedTo());
if (attachedTo == null || !(spellAbility.getTargets().get(0)).canTarget(perm.getControllerId(), perm.getAttachedTo(), spellAbility, this)) {
if (movePermanentToGraveyardWithInfo(perm)) {
if (attachedTo != null) {
attachedTo.removeAttachment(perm.getId(), null, this);
}
somethingHappened = true;
}
}
}
}
}
}
// and it isn't the source of a chapter ability that has triggered but not yet left the stack, that Saga's controller sacrifices it.
if (perm.hasSubtype(SubType.SAGA, this)) {
int maxChapter = perm.getAbilities(this).stream().filter(SagaAbility.class::isInstance).map(SagaAbility.class::cast).map(SagaAbility::getMaxChapter).mapToInt(SagaChapter::getNumber).max().orElse(0);
boolean sacSaga = maxChapter <= perm.getCounters(this).getCount(CounterType.LORE) && this.getStack().stream().filter(SagaAbility::isChapterAbility).map(StackObject::getSourceId).noneMatch(perm.getId()::equals) && this.state.getTriggered(perm.getControllerId()).stream().filter(SagaAbility::isChapterAbility).map(Ability::getSourceId).noneMatch(perm.getId()::equals);
if (sacSaga) {
// After the last chapter ability has left the stack, you'll sacrifice the Saga
perm.sacrifice(null, this);
somethingHappened = true;
}
}
if (this.getState().isLegendaryRuleActive() && StaticFilters.FILTER_PERMANENT_LEGENDARY.match(perm, this)) {
legendary.add(perm);
}
if (StaticFilters.FILTER_PERMANENT_EQUIPMENT.match(perm, this)) {
// 20091005 - 704.5p, 702.14d
if (perm.getAttachedTo() != null) {
Permanent attachedTo = getPermanent(perm.getAttachedTo());
if (attachedTo != null) {
for (Ability ability : perm.getAbilities(this)) {
if (ability instanceof AttachableToRestrictedAbility) {
if (!((AttachableToRestrictedAbility) ability).canEquip(attachedTo, null, this)) {
attachedTo = null;
break;
}
}
}
}
if (attachedTo == null || !attachedTo.getAttachments().contains(perm.getId())) {
UUID wasAttachedTo = perm.getAttachedTo();
perm.attachTo(null, null, this);
fireEvent(new UnattachedEvent(wasAttachedTo, perm.getId(), perm, null));
} else if (!attachedTo.isCreature(this) || attachedTo.hasProtectionFrom(perm, this)) {
if (attachedTo.removeAttachment(perm.getId(), null, this)) {
somethingHappened = true;
}
}
}
}
if (StaticFilters.FILTER_PERMANENT_FORTIFICATION.match(perm, this)) {
if (perm.getAttachedTo() != null) {
Permanent land = getPermanent(perm.getAttachedTo());
if (land == null || !land.getAttachments().contains(perm.getId())) {
perm.attachTo(null, null, this);
} else if (!land.isLand(this) || land.hasProtectionFrom(perm, this)) {
if (land.removeAttachment(perm.getId(), null, this)) {
somethingHappened = true;
}
}
}
}
// it becomes unattached and remains on the battlefield.
if (!perm.getAttachments().isEmpty()) {
for (UUID attachmentId : perm.getAttachments()) {
Permanent attachment = getPermanent(attachmentId);
if (attachment != null && (attachment.isCreature(this) || !(attachment.hasSubtype(SubType.AURA, this) || attachment.hasSubtype(SubType.EQUIPMENT, this) || attachment.hasSubtype(SubType.FORTIFICATION, this)))) {
if (perm.removeAttachment(attachment.getId(), null, this)) {
somethingHappened = true;
break;
}
}
}
}
// 20110501 - 704.5r
if (perm.getCounters(this).containsKey(CounterType.P1P1) && perm.getCounters(this).containsKey(CounterType.M1M1)) {
int p1p1 = perm.getCounters(this).getCount(CounterType.P1P1);
int m1m1 = perm.getCounters(this).getCount(CounterType.M1M1);
int min = Math.min(p1p1, m1m1);
perm.getCounters(this).removeCounter(CounterType.P1P1, min);
perm.getCounters(this).removeCounter(CounterType.M1M1, min);
}
// has more than N counters of that kind on it, all but N of those counters are removed from it.
for (Ability ability : perm.getAbilities(this)) {
if (ability instanceof CantHaveMoreThanAmountCountersSourceAbility) {
CantHaveMoreThanAmountCountersSourceAbility counterAbility = (CantHaveMoreThanAmountCountersSourceAbility) ability;
int count = perm.getCounters(this).getCount(counterAbility.getCounterType());
if (count > counterAbility.getAmount()) {
perm.removeCounters(counterAbility.getCounterType().getName(), count - counterAbility.getAmount(), counterAbility, this);
somethingHappened = true;
}
}
}
}
if (legendary.size() > 1) {
// don't bother checking if less than 2 legends in play
for (Permanent legend : legendary) {
FilterPermanent filterLegendName = new FilterPermanent();
filterLegendName.add(SuperType.LEGENDARY.getPredicate());
filterLegendName.add(new NamePredicate(legend.getName()));
filterLegendName.add(new ControllerIdPredicate(legend.getControllerId()));
if (getBattlefield().contains(filterLegendName, null, legend.getControllerId(), this, 2)) {
if (!replaceEvent(GameEvent.getEvent(GameEvent.EventType.DESTROY_PERMANENT_BY_LEGENDARY_RULE, legend.getId(), legend.getControllerId()))) {
Player controller = this.getPlayer(legend.getControllerId());
if (controller != null) {
Target targetLegendaryToKeep = new TargetPermanent(filterLegendName);
targetLegendaryToKeep.setTargetName(legend.getName() + " to keep (Legendary Rule)?");
controller.chooseTarget(Outcome.Benefit, targetLegendaryToKeep, null, this);
for (Permanent dupLegend : getBattlefield().getActivePermanents(filterLegendName, legend.getControllerId(), this)) {
if (!targetLegendaryToKeep.getTargets().contains(dupLegend.getId())) {
movePermanentToGraveyardWithInfo(dupLegend);
}
}
}
return true;
}
}
}
}
// 704.5k - World Enchantments
if (worldEnchantment.size() > 1) {
int newestCard = -1;
Set<UUID> controllerIdOfNewest = new HashSet<>();
Permanent newestPermanent = null;
for (Permanent permanent : worldEnchantment) {
if (newestCard == -1) {
newestCard = permanent.getCreateOrder();
newestPermanent = permanent;
controllerIdOfNewest.clear();
controllerIdOfNewest.add(permanent.getControllerId());
} else if (newestCard < permanent.getCreateOrder()) {
newestCard = permanent.getCreateOrder();
newestPermanent = permanent;
controllerIdOfNewest.clear();
controllerIdOfNewest.add(permanent.getControllerId());
} else if (newestCard == permanent.getCreateOrder()) {
// In the event of a tie for the shortest amount of time, all are put into their owners’ graveyards. This is called the “world rule.”
newestPermanent = null;
controllerIdOfNewest.add(permanent.getControllerId());
}
}
for (UUID controllerId : controllerIdOfNewest) {
PlayerList newestPermanentControllerRange = state.getPlayersInRange(controllerId, this);
// 801.12 The "world rule" applies to a permanent only if other world permanents are within its controller's range of influence.
for (Permanent permanent : worldEnchantment) {
if (newestPermanentControllerRange.contains(permanent.getControllerId()) && !Objects.equals(newestPermanent, permanent)) {
movePermanentToGraveyardWithInfo(permanent);
somethingHappened = true;
}
}
}
}
// This is not a state-based action but it's unclear where else to put it
if (hasDayNight()) {
for (Permanent permanent : getBattlefield().getAllActivePermanents()) {
if ((permanent.getAbilities(this).containsClass(DayboundAbility.class) && !state.isDaytime()) || (permanent.getAbilities(this).containsClass(NightboundAbility.class) && state.isDaytime())) {
somethingHappened = permanent.transform(null, this, true) || somethingHappened;
}
}
}
// TODO: implement the rest
return somethingHappened;
}
use of mage.game.stack.StackObject in project mage by magefree.
the class GameState method getValue.
public String getValue(boolean useHidden) {
StringBuilder sb = threadLocalBuilder.get();
sb.append(turn.getValue(turnNum));
sb.append(activePlayerId).append(priorityPlayerId).append(playerByOrderId);
for (Player player : players.values()) {
sb.append("player").append(player.getLife()).append("hand");
if (useHidden) {
sb.append(player.getHand());
} else {
sb.append(player.getHand().size());
}
sb.append("library").append(player.getLibrary().size()).append("graveyard").append(player.getGraveyard());
}
sb.append("permanents");
for (Permanent permanent : battlefield.getAllPermanents()) {
sb.append(permanent.getValue(this));
}
sb.append("spells");
for (StackObject spell : stack) {
sb.append(spell.getControllerId()).append(spell.getName());
}
for (ExileZone zone : exile.getExileZones()) {
sb.append("exile").append(zone.getName()).append(zone);
}
sb.append("combat");
for (CombatGroup group : combat.getGroups()) {
sb.append(group.getDefenderId()).append(group.getAttackers()).append(group.getBlockers());
}
return sb.toString();
}
Aggregations