mirror of
https://github.com/magefree/mage.git
synced 2025-12-23 12:02:01 -08:00
- Fixed #7572
This commit is contained in:
parent
ee47bc7c0f
commit
ffe75f77ed
5 changed files with 69 additions and 55 deletions
|
|
@ -20,6 +20,7 @@ import java.util.UUID;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
|
|
@ -82,6 +83,12 @@ class CurseOfTheSwineEffect extends OneShotEffect {
|
|||
Map<UUID, Integer> playersWithTargets = new HashMap<>();
|
||||
for (UUID targetId : this.getTargetPointer().getTargets(game, source)) {
|
||||
Permanent creature = game.getPermanent(targetId);
|
||||
// handle creature tokens here due to LKI being non-existent for them
|
||||
// TODO implement a way to verify that tokens are indeed exiled from the battlefield
|
||||
if (creature instanceof PermanentToken) {
|
||||
playersWithTargets.put(creature.getControllerId(),
|
||||
playersWithTargets.getOrDefault(creature.getControllerId(), 0) + 1);
|
||||
}
|
||||
if (creature != null) {
|
||||
creaturesToExile.add(creature);
|
||||
}
|
||||
|
|
@ -93,6 +100,7 @@ class CurseOfTheSwineEffect extends OneShotEffect {
|
|||
// Count all creatures actually exiled and add them to the player's count
|
||||
for (Card card : creaturesToExile.getCards(game)) {
|
||||
Permanent lkiP = game.getPermanentOrLKIBattlefield(card.getId());
|
||||
// note that tokens have no LKI once they are moved from the battlefield so they are handled earlier
|
||||
if (lkiP != null
|
||||
&& game.getState().getZone(lkiP.getId()) == Zone.EXILED) {
|
||||
playersWithTargets.put(lkiP.getControllerId(),
|
||||
|
|
@ -108,28 +116,3 @@ class CurseOfTheSwineEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
if (controller != null) {
|
||||
Map<UUID, Integer> playersWithTargets = new HashMap<>();
|
||||
for (UUID targetId : this.getTargetPointer().getTargets(game, source)) {
|
||||
Permanent creature = game.getPermanent(targetId);
|
||||
if (creature != null) {
|
||||
if (controller.moveCards(creature, Zone.EXILED, source, game)) {
|
||||
playersWithTargets.put(creature.getControllerId(),
|
||||
playersWithTargets.getOrDefault(creature.getControllerId(), 0) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Boar2Token swineToken = new Boar2Token();
|
||||
for (Map.Entry<UUID, Integer> exiledByController : playersWithTargets.entrySet()) {
|
||||
swineToken.putOntoBattlefield(exiledByController.getValue(), game, source, exiledByController.getKey());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.ForetellAbility;
|
||||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
|
|
@ -19,10 +17,11 @@ import mage.game.Game;
|
|||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeGroupEvent;
|
||||
import mage.game.permanent.token.SpiritWhiteToken;
|
||||
import mage.watchers.common.ForetoldWatcher;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.keyword.ForetellAbility;
|
||||
import mage.watchers.common.ForetoldWatcher;
|
||||
|
||||
/**
|
||||
* @author jeffwadsworth
|
||||
|
|
@ -49,7 +48,7 @@ public final class RanarTheEverWatchful extends CardImpl {
|
|||
this.addAbility(ability);
|
||||
|
||||
// Whenever you exile one or more cards from your hand and/or permanents from the battlefield, create a 1/1 white Spirit creature token with flying.
|
||||
this.addAbility(new RanarTheEverWatchfulTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken())));
|
||||
this.addAbility(new RanarTheEverWatchfulTriggeredAbility());
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +87,7 @@ class RanarTheEverWatchfulCostReductionEffect extends CostModificationEffectImpl
|
|||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
ForetoldWatcher watcher = game.getState().getWatcher(ForetoldWatcher.class);
|
||||
ForetoldWatcher watcher = game.getState().getWatcher(ForetoldWatcher.class, source.getControllerId());
|
||||
return (watcher != null
|
||||
&& watcher.countNumberForetellThisTurn() == 0
|
||||
&& abilityToModify.isControlledBy(source.getControllerId())
|
||||
|
|
@ -98,8 +97,8 @@ class RanarTheEverWatchfulCostReductionEffect extends CostModificationEffectImpl
|
|||
|
||||
class RanarTheEverWatchfulTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
RanarTheEverWatchfulTriggeredAbility(Effect effect) {
|
||||
super(Zone.BATTLEFIELD, effect, false);
|
||||
RanarTheEverWatchfulTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken()), false);
|
||||
}
|
||||
|
||||
RanarTheEverWatchfulTriggeredAbility(final RanarTheEverWatchfulTriggeredAbility ability) {
|
||||
|
|
@ -119,16 +118,15 @@ class RanarTheEverWatchfulTriggeredAbility extends TriggeredAbilityImpl {
|
|||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
ZoneChangeGroupEvent zEvent = (ZoneChangeGroupEvent) event;
|
||||
if (zEvent.getToZone() != Zone.EXILED || zEvent.getCards() == null) {
|
||||
final int numberExiled = zEvent.getCards().size() + zEvent.getTokens().size();
|
||||
if (zEvent.getToZone() != Zone.EXILED
|
||||
|| numberExiled == 0) {
|
||||
return false;
|
||||
}
|
||||
switch (zEvent.getFromZone()) {
|
||||
case BATTLEFIELD:
|
||||
return isControlledBy(game.getControllerId(zEvent.getSourceId()))
|
||||
&& zEvent
|
||||
.getCards()
|
||||
.stream()
|
||||
.anyMatch(Objects::nonNull);
|
||||
return controllerId.equals(zEvent.getSource().getControllerId())
|
||||
&& numberExiled > 0; // must include both card permanents and tokens on the battlefield
|
||||
case HAND:
|
||||
return zEvent
|
||||
.getCards()
|
||||
|
|
@ -142,8 +140,8 @@ class RanarTheEverWatchfulTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever one or more cards are put into exile from your hand " +
|
||||
"or a spell or ability you control exiles one or more permanents from the battlefield, " +
|
||||
"create a 1/1 white Spirit creature token with flying.";
|
||||
return "Whenever one or more cards are put into exile from your hand "
|
||||
+ "or a spell or ability you control exiles one or more permanents from the battlefield, "
|
||||
+ "create a 1/1 white Spirit creature token with flying.";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -816,12 +816,14 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
private final Zone toZone;
|
||||
private final UUID sourceId;
|
||||
private final UUID playerId;
|
||||
Ability source;
|
||||
|
||||
public ZoneChangeData(UUID sourceId, UUID playerId, Zone fromZone, Zone toZone) {
|
||||
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
|
||||
|
|
@ -829,7 +831,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
return (this.fromZone.ordinal() + 1) * 1
|
||||
+ (this.toZone.ordinal() + 1) * 10
|
||||
+ (this.sourceId != null ? this.sourceId.hashCode() : 0)
|
||||
+ (this.playerId != null ? this.playerId.hashCode() : 0);
|
||||
+ (this.source != null ? this.source.hashCode() : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -839,7 +841,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
return this.fromZone == data.fromZone
|
||||
&& this.toZone == data.toZone
|
||||
&& Objects.equals(this.sourceId, data.sourceId)
|
||||
&& Objects.equals(this.playerId, data.playerId);
|
||||
&& Objects.equals(this.source, data.source);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -850,7 +852,12 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
for (GameEvent event : events) {
|
||||
if (event instanceof ZoneChangeEvent) {
|
||||
ZoneChangeEvent castEvent = (ZoneChangeEvent) event;
|
||||
ZoneChangeData key = new ZoneChangeData(castEvent.getSourceId(), castEvent.getPlayerId(), castEvent.getFromZone(), castEvent.getToZone());
|
||||
ZoneChangeData key = new ZoneChangeData(
|
||||
castEvent.getSource(),
|
||||
castEvent.getSourceId(),
|
||||
castEvent.getPlayerId(),
|
||||
castEvent.getFromZone(),
|
||||
castEvent.getToZone());
|
||||
if (eventsByKey.containsKey(key)) {
|
||||
eventsByKey.get(key).add(event);
|
||||
} else {
|
||||
|
|
@ -863,20 +870,30 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
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(); ) {
|
||||
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.playerId, eventData.fromZone, eventData.toZone);
|
||||
System.out.println("The movedCards and movedTokens : " + movedCards + " " + movedTokens);
|
||||
ZoneChangeGroupEvent event = new ZoneChangeGroupEvent(
|
||||
movedCards,
|
||||
movedTokens,
|
||||
eventData.sourceId,
|
||||
eventData.source,
|
||||
eventData.playerId,
|
||||
eventData.fromZone,
|
||||
eventData.toZone);
|
||||
groupEvents.add(event);
|
||||
}
|
||||
}
|
||||
|
|
@ -928,7 +945,8 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
* span
|
||||
*
|
||||
* @param ability
|
||||
* @param sourceId - if source object can be moved between zones then you must set it here (each game cycle clear all source related triggers)
|
||||
* @param sourceId - if source object can be moved between zones then you
|
||||
* must set it here (each game cycle clear all source related triggers)
|
||||
* @param attachedTo
|
||||
*/
|
||||
public void addAbility(Ability ability, UUID sourceId, MageObject attachedTo) {
|
||||
|
|
@ -1046,7 +1064,8 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
/**
|
||||
* Return values list starting with searching key.
|
||||
* <p>
|
||||
* Usage example: if you want to find all saved values from related ability/effect
|
||||
* Usage example: if you want to find all saved values from related
|
||||
* ability/effect
|
||||
*
|
||||
* @param startWithValue
|
||||
* @return
|
||||
|
|
@ -1133,8 +1152,8 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
* @param attachedTo
|
||||
* @param ability
|
||||
* @param copyAbility copies non MageSingleton abilities before adding to
|
||||
* state (allows to have multiple instances in one object,
|
||||
* e.g. false param will simulate keyword/singletone)
|
||||
* state (allows to have multiple instances in one object, e.g. false param
|
||||
* will simulate keyword/singleton)
|
||||
*/
|
||||
public void addOtherAbility(Card attachedTo, Ability ability, boolean copyAbility) {
|
||||
checkWrongDynamicAbilityUsage(attachedTo, ability);
|
||||
|
|
@ -1271,7 +1290,8 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Make full copy of the card and all of the card's parts and put to the game.
|
||||
* Make full copy of the card and all of the card's parts and put to the
|
||||
* game.
|
||||
*
|
||||
* @param mainCardToCopy
|
||||
* @param newController
|
||||
|
|
|
|||
|
|
@ -36,7 +36,14 @@ public final class ZonesHandler {
|
|||
} else {
|
||||
cards.add(targetCard);
|
||||
}
|
||||
game.fireEvent(new ZoneChangeGroupEvent(cards, tokens, info.event.getSourceId(), info.event.getPlayerId(), info.event.getFromZone(), info.event.getToZone()));
|
||||
game.fireEvent(new ZoneChangeGroupEvent(
|
||||
cards,
|
||||
tokens,
|
||||
info.event.getSourceId(),
|
||||
info.event.getSource(),
|
||||
info.event.getPlayerId(),
|
||||
info.event.getFromZone(),
|
||||
info.event.getToZone()));
|
||||
// normal movement
|
||||
game.fireEvent(info.event);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ package mage.game.events;
|
|||
import mage.cards.Card;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
|
|
@ -16,13 +16,15 @@ public class ZoneChangeGroupEvent extends GameEvent {
|
|||
private final Zone toZone;
|
||||
private final Set<Card> cards;
|
||||
private final Set<PermanentToken> tokens;
|
||||
/* added this */ Ability source;
|
||||
|
||||
public ZoneChangeGroupEvent(Set<Card> cards, Set<PermanentToken> tokens, UUID sourceId, UUID playerId, Zone fromZone, Zone toZone) {
|
||||
public ZoneChangeGroupEvent(Set<Card> cards, Set<PermanentToken> tokens, UUID sourceId, Ability source, UUID playerId, Zone fromZone, Zone toZone) {
|
||||
super(GameEvent.EventType.ZONE_CHANGE_GROUP, null, null, playerId);
|
||||
this.fromZone = fromZone;
|
||||
this.toZone = toZone;
|
||||
this.cards = cards;
|
||||
this.tokens = tokens;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Zone getFromZone() {
|
||||
|
|
@ -40,5 +42,9 @@ public class ZoneChangeGroupEvent extends GameEvent {
|
|||
public Set<PermanentToken> getTokens() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
public Ability getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue