This commit is contained in:
jeffwadsworth 2021-07-14 16:44:28 -05:00
parent ee47bc7c0f
commit ffe75f77ed
5 changed files with 69 additions and 55 deletions

View file

@ -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;
}
*/

View file

@ -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.";
}
}

View file

@ -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

View file

@ -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;

View file

@ -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;
}
}