Implemented Mairsil properly

This commit is contained in:
Evan Kranzler 2017-08-22 09:27:07 -04:00
parent f33d9006c6
commit 8d85189262
2 changed files with 195 additions and 103 deletions

View file

@ -27,14 +27,21 @@
*/
package mage.cards.m;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.ActivatedAbilityImpl;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
@ -46,6 +53,7 @@ import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.SuperType;
import mage.constants.TargetController;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.FilterCard;
@ -54,11 +62,15 @@ import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.other.CounterCardPredicate;
import mage.filter.predicate.other.OwnerPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.StackAbility;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetCardInHand;
import mage.target.common.TargetCardInYourGraveyard;
import mage.watchers.Watcher;
/**
*
@ -79,7 +91,9 @@ public class MairsilThePretender extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new MairsilThePretenderExileEffect(), true));
// Mairsil, the Pretender has all activated abilities of all cards you own in exile with cage counters on them. You may activate each of those abilities only once each turn.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MairsilThePretenderGainAbilitiesEffect()));
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new MairsilThePretenderGainAbilitiesEffect());
ability.addEffect(new MairsilThePretenderRuleModifyingEffect());
this.addAbility(ability, new MairsilThePretenderWatcher());
}
public MairsilThePretender(final MairsilThePretender card) {
@ -167,8 +181,11 @@ class MairsilThePretenderGainAbilitiesEffect extends ContinuousEffectImpl {
if (filter.match(card, game)) {
for (Ability ability : card.getAbilities()) {
if (ability instanceof ActivatedAbility) {
ActivatedAbilityImpl copyAbility = (ActivatedAbilityImpl) ability;
copyAbility.setMaxActivationsPerTurn(1);
UUID originaId = ability.getId();
ActivatedAbility copyAbility = (ActivatedAbility) ability.copy();
Effect effect = new DoNothingEffect();
effect.setValue("key", originaId);
copyAbility.addEffect(effect);
perm.addAbility(copyAbility, card.getId(), game);
}
}
@ -184,3 +201,135 @@ class MairsilThePretenderGainAbilitiesEffect extends ContinuousEffectImpl {
return new MairsilThePretenderGainAbilitiesEffect(this);
}
}
class MairsilThePretenderWatcher extends Watcher {
public final Map<UUID, Set<UUID>> activatedThisTurnAbilities = new HashMap<>();
public MairsilThePretenderWatcher() {
super(MairsilThePretenderWatcher.class.getSimpleName(), WatcherScope.GAME);
}
public MairsilThePretenderWatcher(final MairsilThePretenderWatcher watcher) {
super(watcher);
for (Entry<UUID, Set<UUID>> entry : watcher.activatedThisTurnAbilities.entrySet()) {
activatedThisTurnAbilities.put(entry.getKey(), entry.getValue());
}
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.ZONE_CHANGE && event instanceof ZoneChangeEvent) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.getFromZone() == Zone.BATTLEFIELD) {
Permanent permanent = zEvent.getTarget();
if (permanent != null) {
this.activatedThisTurnAbilities.remove(permanent.getId());
}
}
}
if (event.getType() == GameEvent.EventType.ACTIVATED_ABILITY) {
Set<UUID> permAbilities;
if (activatedThisTurnAbilities.keySet().contains(event.getSourceId())) {
permAbilities = activatedThisTurnAbilities.get(event.getSourceId());
} else {
permAbilities = new HashSet<>();
}
StackAbility ability = (StackAbility) game.getStack().getStackObject(event.getSourceId());
if (ability != null && ability.getStackAbility().isActivated()) {
for (Effect effect : ability.getAllEffects()) {
if (effect instanceof DoNothingEffect) {
permAbilities.add((UUID) effect.getValue("key"));
this.activatedThisTurnAbilities.put(event.getSourceId(), permAbilities);
}
}
}
}
}
@Override
public void reset() {
activatedThisTurnAbilities.clear();
}
public Map<UUID, Set<UUID>> getActivatedThisTurnAbilities() {
return this.activatedThisTurnAbilities;
}
@Override
public MairsilThePretenderWatcher copy() {
return new MairsilThePretenderWatcher(this);
}
}
class MairsilThePretenderRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl {
public MairsilThePretenderRuleModifyingEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
}
public MairsilThePretenderRuleModifyingEffect(final MairsilThePretenderRuleModifyingEffect effect) {
super(effect);
}
@Override
public MairsilThePretenderRuleModifyingEffect copy() {
return new MairsilThePretenderRuleModifyingEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ACTIVATE_ABILITY;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Optional<Ability> ability = game.getAbility(event.getTargetId(), event.getSourceId());
MairsilThePretenderWatcher watcher = (MairsilThePretenderWatcher) game.getState().getWatchers().get(MairsilThePretenderWatcher.class.getSimpleName());
if (watcher != null && ability != null && ability.isPresent()) {
for (Effect effect : ability.get().getAllEffects()) {
if (effect instanceof DoNothingEffect) {
UUID originalID = (UUID) effect.getValue("key");
if (watcher.getActivatedThisTurnAbilities().keySet().contains(event.getSourceId())) {
if (watcher.getActivatedThisTurnAbilities().get(event.getSourceId()).contains(originalID)) {
return true;
}
}
}
}
}
return false;
}
@Override
public String getInfoMessage(Ability source, GameEvent event, Game game) {
return "This ability can only be activated once each turn.";
}
}
class DoNothingEffect extends OneShotEffect {
DoNothingEffect() {
super(Outcome.Neutral);
}
DoNothingEffect(final DoNothingEffect effect) {
super(effect);
}
@Override
public DoNothingEffect copy() {
return new DoNothingEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
}

View file

@ -45,7 +45,6 @@ import mage.constants.Zone;
import mage.game.Game;
import mage.game.command.Emblem;
import mage.game.permanent.Permanent;
import mage.util.CardUtil;
/**
*
@ -53,22 +52,10 @@ import mage.util.CardUtil;
*/
public abstract class ActivatedAbilityImpl extends AbilityImpl implements ActivatedAbility {
static class ActivationInfo {
public int turnNum;
public int activationCounter;
public ActivationInfo(int turnNum, int activationCounter) {
this.turnNum = turnNum;
this.activationCounter = activationCounter;
}
}
protected TimingRule timing = TimingRule.INSTANT;
protected TargetController mayActivate = TargetController.YOU;
protected UUID activatorId;
protected boolean checkPlayableMode;
private int maxActivationsPerTurn;
protected ActivatedAbilityImpl(AbilityType abilityType, Zone zone) {
super(abilityType, zone);
@ -172,82 +159,55 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
public abstract ActivatedAbilityImpl copy();
@Override
public boolean activate(Game game, boolean noMana) {
if (hasMoreActivationsThisTurn(game)) {
if (super.activate(game, noMana)) {
ActivatedAbilityImpl.ActivationInfo activationInfo = getActivationInfo(game);
if (activationInfo == null) {
activationInfo = new ActivatedAbilityImpl.ActivationInfo(game.getTurnNum(), 1);
} else if (activationInfo.turnNum != game.getTurnNum()) {
activationInfo.turnNum = game.getTurnNum();
activationInfo.activationCounter = 1;
} else {
activationInfo.activationCounter++;
public boolean canActivate(UUID playerId, Game game) {
//20091005 - 602.2
switch (mayActivate) {
case ANY:
break;
case NOT_YOU:
if (controlsAbility(playerId, game)) {
return false;
}
setActivationInfo(activationInfo, game);
break;
case OPPONENT:
if (!game.getPlayer(controllerId).hasOpponent(playerId, game)) {
return false;
}
break;
case OWNER:
Permanent permanent = game.getPermanent(getSourceId());
if (!permanent.getOwnerId().equals(playerId)) {
return false;
}
break;
case YOU:
if (!controlsAbility(playerId, game)) {
return false;
}
break;
case CONTROLLER_ATTACHED_TO:
Permanent enchantment = game.getPermanent(getSourceId());
if (enchantment != null && enchantment.getAttachedTo() != null) {
Permanent enchanted = game.getPermanent(enchantment.getAttachedTo());
if (enchanted != null && enchanted.getControllerId().equals(playerId)) {
break;
}
}
return false;
}
//20091005 - 602.5d/602.5e
if (timing == TimingRule.INSTANT || game.canPlaySorcery(playerId)
|| game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_AS_INSTANT, this, controllerId, game)) {
if (costs.canPay(this, sourceId, playerId, game) && canChooseTarget(game)) {
this.activatorId = playerId;
return true;
}
}
return false;
}
@Override
public boolean canActivate(UUID playerId, Game game) {
//20091005 - 602.2
if (hasMoreActivationsThisTurn(game)) {
switch (mayActivate) {
case ANY:
break;
case NOT_YOU:
if (controlsAbility(playerId, game)) {
return false;
}
break;
case OPPONENT:
if (!game.getPlayer(controllerId).hasOpponent(playerId, game)) {
return false;
}
break;
case OWNER:
Permanent permanent = game.getPermanent(getSourceId());
if (!permanent.getOwnerId().equals(playerId)) {
return false;
}
break;
case YOU:
if (!controlsAbility(playerId, game)) {
return false;
}
break;
case CONTROLLER_ATTACHED_TO:
Permanent enchantment = game.getPermanent(getSourceId());
if (enchantment != null && enchantment.getAttachedTo() != null) {
Permanent enchanted = game.getPermanent(enchantment.getAttachedTo());
if (enchanted != null && enchanted.getControllerId().equals(playerId)) {
break;
}
}
return false;
}
//20091005 - 602.5d/602.5e
if (timing == TimingRule.INSTANT || game.canPlaySorcery(playerId)
|| game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.ACTIVATE_AS_INSTANT, this, controllerId, game)) {
if (costs.canPay(this, sourceId, playerId, game) && canChooseTarget(game)) {
this.activatorId = playerId;
return true;
}
}
}
return false;
}
private boolean hasMoreActivationsThisTurn(Game game) {
ActivatedAbilityImpl.ActivationInfo activationInfo = this.getActivationInfo(game);
return activationInfo == null || activationInfo.turnNum != game.getTurnNum() || activationInfo.activationCounter < maxActivationsPerTurn;
}
@Override
public ManaOptions getMinimumCostToActivate(UUID playerId, Game game) {
return getManaCostsToPay().getOptions();
@ -295,21 +255,4 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
return checkPlayableMode;
}
public void setMaxActivationsPerTurn(int maxActivationsPerTurn) {
this.maxActivationsPerTurn = maxActivationsPerTurn;
}
private ActivatedAbilityImpl.ActivationInfo getActivationInfo(Game game) {
Integer turnNum = (Integer) game.getState().getValue(CardUtil.getCardZoneString("activationsTurn"+this.getOriginalId().toString(), sourceId, game));
Integer activationCount = (Integer) game.getState().getValue(CardUtil.getCardZoneString("activationsCount"+this.getOriginalId().toString(), sourceId, game));
if (turnNum == null || activationCount == null) {
return null;
}
return new ActivatedAbilityImpl.ActivationInfo(turnNum, activationCount);
}
private void setActivationInfo(ActivatedAbilityImpl.ActivationInfo activationInfo, Game game) {
game.getState().setValue(CardUtil.getCardZoneString("activationsTurn"+this.getOriginalId().toString(), sourceId, game), activationInfo.turnNum);
game.getState().setValue(CardUtil.getCardZoneString("activationsCount"+this.getOriginalId().toString(), sourceId, game), activationInfo.activationCounter);
}
}