use of mage.constants.Zone in project mage by magefree.
the class ShuffleIntoLibrarySourceEffect method apply.
@Override
public boolean apply(Game game, Ability source) {
MageObject mageObject = source.getSourceObjectIfItStillExists(game);
if (mageObject != null) {
Zone fromZone = game.getState().getZone(mageObject.getId());
Player owner;
if (mageObject instanceof Permanent) {
owner = game.getPlayer(((Permanent) mageObject).getOwnerId());
if (owner != null) {
owner.moveCardToLibraryWithInfo((Permanent) mageObject, source, game, fromZone, true, true);
owner.shuffleLibrary(source, game);
return true;
}
} else if (mageObject instanceof Card) {
owner = game.getPlayer(((Card) mageObject).getOwnerId());
if (owner != null) {
owner.moveCardToLibraryWithInfo((Card) mageObject, source, game, fromZone, true, true);
owner.shuffleLibrary(source, game);
return true;
}
}
}
return false;
}
use of mage.constants.Zone in project mage by magefree.
the class GameState method createEventGroups.
public List<GameEvent> createEventGroups(List<GameEvent> events, Game game) {
class ZoneChangeData {
private final Zone fromZone;
private final Zone toZone;
private final UUID sourceId;
private final UUID playerId;
Ability source;
public ZoneChangeData(Ability source, UUID sourceId, UUID playerId, Zone fromZone, Zone toZone) {
this.sourceId = sourceId;
this.playerId = playerId;
this.fromZone = fromZone;
this.toZone = toZone;
this.source = source;
}
@Override
public int hashCode() {
return (this.fromZone.ordinal() + 1) * 1 + (this.toZone.ordinal() + 1) * 10 + (this.sourceId != null ? this.sourceId.hashCode() : 0) + (this.source != null ? this.source.hashCode() : 0);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ZoneChangeData) {
ZoneChangeData data = (ZoneChangeData) obj;
return this.fromZone == data.fromZone && this.toZone == data.toZone && Objects.equals(this.sourceId, data.sourceId) && Objects.equals(this.source, data.source);
}
return false;
}
}
Map<ZoneChangeData, List<GameEvent>> eventsByKey = new HashMap<>();
List<GameEvent> groupEvents = new LinkedList<>();
for (GameEvent event : events) {
if (event instanceof ZoneChangeEvent) {
ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
ZoneChangeData key = new ZoneChangeData(castEvent.getSource(), castEvent.getSourceId(), castEvent.getPlayerId(), castEvent.getFromZone(), castEvent.getToZone());
if (eventsByKey.containsKey(key)) {
eventsByKey.get(key).add(event);
} else {
List<GameEvent> list = new LinkedList<>();
list.add(event);
eventsByKey.put(key, list);
}
}
}
for (Map.Entry<ZoneChangeData, List<GameEvent>> entry : eventsByKey.entrySet()) {
Set<Card> movedCards = new LinkedHashSet<>();
Set<PermanentToken> movedTokens = new LinkedHashSet<>();
for (Iterator<GameEvent> it = entry.getValue().iterator(); it.hasNext(); ) {
GameEvent event = it.next();
ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
UUID targetId = castEvent.getTargetId();
Card card = ZonesHandler.getTargetCard(game, targetId);
if (card instanceof PermanentToken) {
movedTokens.add((PermanentToken) card);
} else if (game.getObject(targetId) instanceof PermanentToken) {
movedTokens.add((PermanentToken) game.getObject(targetId));
} else if (card != null) {
movedCards.add(card);
}
}
ZoneChangeData eventData = entry.getKey();
if (!movedCards.isEmpty() || !movedTokens.isEmpty()) {
ZoneChangeGroupEvent event = new ZoneChangeGroupEvent(movedCards, movedTokens, eventData.sourceId, eventData.source, eventData.playerId, eventData.fromZone, eventData.toZone);
groupEvents.add(event);
}
}
return groupEvents;
}
use of mage.constants.Zone in project mage by magefree.
the class AdventureCard method moveToExile.
@Override
public boolean moveToExile(UUID exileId, String name, Ability source, Game game, List<UUID> appliedEffects) {
if (super.moveToExile(exileId, name, source, game, appliedEffects)) {
Zone currentZone = game.getState().getZone(getId());
game.getState().setZone(getSpellCard().getId(), currentZone);
return true;
}
return false;
}
use of mage.constants.Zone in project mage by magefree.
the class ZonesHandler method placeInDestinationZone.
private static void placeInDestinationZone(ZoneChangeInfo info, Game game, int createOrder) {
// Handle unmelded cards
if (info instanceof ZoneChangeInfo.Unmelded) {
ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info;
Zone toZone = null;
for (ZoneChangeInfo subInfo : unmelded.subInfo) {
toZone = subInfo.event.getToZone();
placeInDestinationZone(subInfo, game, createOrder);
}
// We arbitrarily prefer the bottom half card. This should never be relevant.
if (toZone != null) {
game.setZone(unmelded.event.getTargetId(), toZone);
}
return;
}
// Handle normal cases
ZoneChangeEvent event = info.event;
Zone toZone = event.getToZone();
Card targetCard = getTargetCard(game, event.getTargetId());
// moving real cards
Cards cardsToMove = null;
// updating all card's parts (must be ordered LinkedHashMap)
Map<Zone, Cards> cardsToUpdate = new LinkedHashMap<>();
cardsToUpdate.put(toZone, new CardsImpl());
cardsToUpdate.put(Zone.OUTSIDE, new CardsImpl());
// if we're moving a token it shouldn't be put into any zone as an object.
if (!(targetCard instanceof Permanent) && targetCard != null) {
if (targetCard instanceof MeldCard) {
// meld/group cards must be independent (use can choose order)
cardsToMove = ((MeldCard) targetCard).getHalves();
cardsToUpdate.get(toZone).addAll(cardsToMove);
} else if (targetCard instanceof ModalDoubleFacesCard || targetCard instanceof ModalDoubleFacesCardHalf) {
// mdf cards must be moved as single object, but each half must be updated separately
ModalDoubleFacesCard mdfCard = (ModalDoubleFacesCard) targetCard.getMainCard();
cardsToMove = new CardsImpl(mdfCard);
cardsToUpdate.get(toZone).add(mdfCard);
// * right to outside (it helps to ignore all triggers and other effects from that card)
switch(toZone) {
case STACK:
case BATTLEFIELD:
if (targetCard.getId().equals(mdfCard.getLeftHalfCard().getId())) {
// play left
cardsToUpdate.get(toZone).add(mdfCard.getLeftHalfCard());
cardsToUpdate.get(Zone.OUTSIDE).add(mdfCard.getRightHalfCard());
} else if (targetCard.getId().equals(mdfCard.getRightHalfCard().getId())) {
// play right
cardsToUpdate.get(toZone).add(mdfCard.getRightHalfCard());
cardsToUpdate.get(Zone.OUTSIDE).add(mdfCard.getLeftHalfCard());
} else {
// cast mdf (only on stack)
if (!toZone.equals(Zone.STACK)) {
throw new IllegalStateException("Wrong mdf card move to " + toZone + " in placeInDestinationZone");
}
cardsToUpdate.get(toZone).add(mdfCard.getLeftHalfCard());
cardsToUpdate.get(toZone).add(mdfCard.getRightHalfCard());
}
break;
default:
// move all parts
cardsToUpdate.get(toZone).add(mdfCard.getLeftHalfCard());
cardsToUpdate.get(toZone).add(mdfCard.getRightHalfCard());
break;
}
} else {
cardsToMove = new CardsImpl(targetCard);
cardsToUpdate.get(toZone).addAll(cardsToMove);
}
Player owner = game.getPlayer(targetCard.getOwnerId());
switch(toZone) {
case HAND:
for (Card card : cardsToMove.getCards(game)) {
game.getPlayer(card.getOwnerId()).getHand().add(card);
}
break;
case GRAVEYARD:
for (Card card : chooseOrder("order to put in graveyard (last chosen will be on top)", cardsToMove, owner, game)) {
game.getPlayer(card.getOwnerId()).getGraveyard().add(card);
}
break;
case LIBRARY:
if (info instanceof ZoneChangeInfo.Library && ((ZoneChangeInfo.Library) info).top) {
// on top
for (Card card : chooseOrder("order to put on top of library (last chosen will be topmost)", cardsToMove, owner, game)) {
game.getPlayer(card.getOwnerId()).getLibrary().putOnTop(card, game);
}
} else {
// on bottom
for (Card card : chooseOrder("order to put on bottom of library (last chosen will be bottommost)", cardsToMove, owner, game)) {
game.getPlayer(card.getOwnerId()).getLibrary().putOnBottom(card, game);
}
}
break;
case EXILED:
for (Card card : cardsToMove.getCards(game)) {
if (info instanceof ZoneChangeInfo.Exile && ((ZoneChangeInfo.Exile) info).id != null) {
ZoneChangeInfo.Exile exileInfo = (ZoneChangeInfo.Exile) info;
game.getExile().createZone(exileInfo.id, exileInfo.name).add(card);
} else {
game.getExile().getPermanentExile().add(card);
}
}
break;
case COMMAND:
// There should never be more than one card here.
for (Card card : cardsToMove.getCards(game)) {
game.addCommander(new Commander(card));
}
break;
case STACK:
// There should never be more than one card here.
for (Card card : cardsToMove.getCards(game)) {
Spell spell;
if (info instanceof ZoneChangeInfo.Stack && ((ZoneChangeInfo.Stack) info).spell != null) {
spell = ((ZoneChangeInfo.Stack) info).spell;
} else {
spell = new Spell(card, card.getSpellAbility().copy(), card.getOwnerId(), event.getFromZone(), game);
}
spell.syncZoneChangeCounterOnStack(card, game);
game.getStack().push(spell);
game.getState().setZone(spell.getId(), Zone.STACK);
game.getState().setZone(card.getId(), Zone.STACK);
}
break;
case BATTLEFIELD:
Permanent permanent = event.getTarget();
game.addPermanent(permanent, createOrder);
game.getPermanentsEntering().remove(permanent.getId());
break;
default:
throw new UnsupportedOperationException("to Zone " + toZone.toString() + " not supported yet");
}
}
// update zone in main
game.setZone(event.getTargetId(), event.getToZone());
// update zone in other parts (meld cards, mdf half cards)
cardsToUpdate.entrySet().forEach(entry -> {
for (Card card : entry.getValue().getCards(game)) {
if (!card.getId().equals(event.getTargetId())) {
game.setZone(card.getId(), entry.getKey());
}
}
});
// reset meld status
if (targetCard instanceof MeldCard) {
if (event.getToZone() != Zone.BATTLEFIELD) {
((MeldCard) targetCard).setMelded(false, game);
}
}
}
use of mage.constants.Zone in project mage by magefree.
the class ZonesHandler method maybeRemoveFromSourceZone.
private static boolean maybeRemoveFromSourceZone(ZoneChangeInfo info, Game game, Ability source) {
ZoneChangeEvent event = info.event;
// Handle Unmelded Cards
if (info instanceof ZoneChangeInfo.Unmelded) {
ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info;
MeldCard meld = game.getMeldCard(event.getTargetId());
for (Iterator<ZoneChangeInfo> itr = unmelded.subInfo.iterator(); itr.hasNext(); ) {
ZoneChangeInfo subInfo = itr.next();
if (!maybeRemoveFromSourceZone(subInfo, game, source)) {
itr.remove();
} else if (Objects.equals(subInfo.event.getTargetId(), meld.getTopHalfCard().getId())) {
meld.setTopLastZoneChangeCounter(meld.getTopHalfCard().getZoneChangeCounter(game));
} else if (Objects.equals(subInfo.event.getTargetId(), meld.getBottomHalfCard().getId())) {
meld.setBottomLastZoneChangeCounter(meld.getBottomHalfCard().getZoneChangeCounter(game));
}
}
if (unmelded.subInfo.isEmpty()) {
return false;
}
// We arbitrarily prefer the bottom half card. This should never be relevant.
meld.updateZoneChangeCounter(game, unmelded.subInfo.get(unmelded.subInfo.size() - 1).event);
return true;
}
// Handle all normal cases
Card card = getTargetCard(game, event.getTargetId());
if (card == null) {
// If we can't find the card we can't remove it.
return false;
}
boolean success = false;
if (info.faceDown) {
card.setFaceDown(true, game);
} else if (event.getToZone().equals(Zone.BATTLEFIELD)) {
if (!card.isPermanent(game) && (!card.isTransformable() || Boolean.FALSE.equals(game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId())))) {
// Non permanents (Instants, Sorceries, ... stay in the zone they are if an abilty/effect tries to move it to the battlefield
return false;
}
}
if (!game.replaceEvent(event)) {
Zone fromZone = event.getFromZone();
if (event.getToZone() == Zone.BATTLEFIELD) {
// prepare card and permanent
// If needed take attributes from the spell (e.g. color of spell was changed)
card = takeAttributesFromSpell(card, event, game);
// controlling player can be replaced so use event player now
Permanent permanent;
if (card instanceof MeldCard) {
permanent = new PermanentMeld(card, event.getPlayerId(), game);
} else if (card instanceof ModalDoubleFacesCard) {
// main mdf card must be processed before that call (e.g. only halfes can be moved to battlefield)
throw new IllegalStateException("Unexpected trying of move mdf card to battlefield instead half");
} else if (card instanceof Permanent) {
throw new IllegalStateException("Unexpected trying of move permanent to battlefield instead card");
} else {
permanent = new PermanentCard(card, event.getPlayerId(), game);
}
// put onto battlefield with possible counters
game.getPermanentsEntering().put(permanent.getId(), permanent);
card.checkForCountersToAdd(permanent, source, game);
permanent.setTapped(info instanceof ZoneChangeInfo.Battlefield && ((ZoneChangeInfo.Battlefield) info).tapped);
permanent.setFaceDown(info.faceDown, game);
if (info.faceDown) {
card.setFaceDown(false, game);
}
// make sure the controller of all continuous effects of this card are switched to the current controller
game.setScopeRelevant(true);
game.getContinuousEffects().setController(permanent.getId(), permanent.getControllerId());
if (permanent.entersBattlefield(source, game, fromZone, true) && card.removeFromZone(game, fromZone, source)) {
success = true;
event.setTarget(permanent);
} else {
// revert controller to owner if permanent does not enter
game.getContinuousEffects().setController(permanent.getId(), permanent.getOwnerId());
game.getPermanentsEntering().remove(permanent.getId());
}
game.setScopeRelevant(false);
} else if (event.getTarget() != null) {
card.setFaceDown(info.faceDown, game);
Permanent target = event.getTarget();
success = target.removeFromZone(game, fromZone, source) && game.getPlayer(target.getControllerId()).removeFromBattlefield(target, source, game);
} else {
card.setFaceDown(info.faceDown, game);
success = card.removeFromZone(game, fromZone, source);
}
}
if (success) {
// KickerTest do many tests for token's zcc
if (event.getToZone() == Zone.BATTLEFIELD && event.getTarget() != null) {
event.getTarget().updateZoneChangeCounter(game, event);
} else if (!(card instanceof Permanent)) {
card.updateZoneChangeCounter(game, event);
}
}
return success;
}
Aggregations