diff --git a/Mage.Sets/src/mage/sets/innistrad/DearlyDeparted.java b/Mage.Sets/src/mage/sets/innistrad/DearlyDeparted.java index 0eac2d4f1c8..318cd74bbb5 100644 --- a/Mage.Sets/src/mage/sets/innistrad/DearlyDeparted.java +++ b/Mage.Sets/src/mage/sets/innistrad/DearlyDeparted.java @@ -33,16 +33,23 @@ import mage.Constants.CardType; import mage.Constants.Rarity; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.Mode; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.common.continious.GainAbilityControlledEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.Effects; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -71,10 +78,8 @@ public class DearlyDeparted extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // As long as Dearly Departed is in your graveyard, each Human creature you control enters the battlefield with an additional +1/+1 counter on it. - Ability ability = new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1))); - ContinuousEffect effect = new GainAbilityControlledEffect(ability, Constants.Duration.WhileInGraveyard, filterHuman); - effect.overrideRuleText(ruleText); - this.addAbility(new SimpleStaticAbility(Constants.Zone.GRAVEYARD, effect)); + this.addAbility(new SimpleStaticAbility(Constants.Zone.GRAVEYARD, + new EntersBattlefieldEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(1)), ruleText))); } public DearlyDeparted(final DearlyDeparted card) { @@ -86,3 +91,80 @@ public class DearlyDeparted extends CardImpl { return new DearlyDeparted(this); } } + +class EntersBattlefieldEffect extends ReplacementEffectImpl { + + protected Effects baseEffects = new Effects(); + protected String text; + + public static final String SOURCE_CAST_SPELL_ABILITY = "sourceCastSpellAbility"; + + public EntersBattlefieldEffect(Effect baseEffect) { + this(baseEffect, ""); + } + + public EntersBattlefieldEffect(Effect baseEffect, String text) { + super(Constants.Duration.OneUse, baseEffect.getOutcome()); + this.baseEffects.add(baseEffect); + this.text = text; + } + + public EntersBattlefieldEffect(EntersBattlefieldEffect effect) { + super(effect); + this.baseEffects = effect.baseEffects.copy(); + this.text = effect.text; + } + + public void addEffect(Effect effect) { + baseEffects.add(effect); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && permanent.getControllerId().equals(source.getControllerId()) && permanent.hasSubtype("Human")) { + setValue("target", permanent); + return true; + } + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Spell spell = game.getStack().getSpell(event.getSourceId()); + for (Effect effect: baseEffects) { + Object target = getValue("target"); + if (target != null && target instanceof Permanent) { + effect.setTargetPointer(new FixedTarget(((Permanent)target).getId())); + if (effect instanceof ContinuousEffect) { + game.addEffect((ContinuousEffect) effect, source); + } + else { + if (spell != null) { + effect.setValue(SOURCE_CAST_SPELL_ABILITY, spell.getSpellAbility()); + } + effect.apply(game, source); + } + } + } + return false; + } + + @Override + public String getText(Mode mode) { + return (text == null || text.isEmpty()) ? baseEffects.getText(mode) : text; + } + + @Override + public EntersBattlefieldEffect copy() { + return new EntersBattlefieldEffect(this); + } + +} diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 40632a15794..21dcec7a57c 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -44,6 +44,7 @@ import mage.cards.Card; import mage.choices.Choice; import mage.choices.Choices; import mage.game.Game; +import mage.game.permanent.PermanentCard; import mage.target.Target; import mage.target.Targets; import org.apache.log4j.Logger; @@ -478,6 +479,19 @@ public abstract class AbilityImpl> implements Ability { } } + MageObject object = game.getObject(getSourceId()); + if (object != null && !object.getAbilities().contains(this)) { + boolean found = false; + // unfortunately we need to handle double faced cards separately and only this way + if (object instanceof PermanentCard && ((PermanentCard)object).canTransform()) { + PermanentCard permanent = (PermanentCard)object; + found = permanent.getSecondCardFace().getAbilities().contains(this) || permanent.getCard().getAbilities().contains(this); + } + if (!found) { + return false; + } + } + // check against current state Zone test = game.getState().getZone(sourceId); return test != null && zone.match(test); diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 765d95668dd..8a987124607 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -65,6 +65,8 @@ public class ContinuousEffects implements Serializable { private final AuraReplacementEffect auraReplacementEffect; private List previous = new ArrayList(); + + private Map sources = new HashMap(); public ContinuousEffects() { applyCounters = new ApplyCountersEffect(); @@ -84,6 +86,9 @@ public class ContinuousEffects implements Serializable { restrictionEffects = effect.restrictionEffects.copy(); asThoughEffects = effect.asThoughEffects.copy(); costModificationEffects = effect.costModificationEffects.copy(); + for (Map.Entry entry : effect.sources.entrySet()) { + sources.put(entry.getKey(), entry.getValue()); + } collectAllEffects(); } @@ -384,9 +389,12 @@ public class ContinuousEffects implements Serializable { } layer = filterLayeredEffects(layerEffects, Layer.AbilityAddingRemovingEffects_6); for (ContinuousEffect effect: layer) { - effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, layeredEffects.getAbility(effect.getId()), game); + layerEffects = getLayeredEffects(game); + if (layerEffects.contains(effect)) { + effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, layeredEffects.getAbility(effect.getId()), game); + } } - layerEffects = getLayeredEffects(game); + layer = filterLayeredEffects(layerEffects, Layer.PTChangingEffects_7); for (ContinuousEffect effect: layer) { effect.apply(Layer.PTChangingEffects_7, SubLayer.SetPT_7b, layeredEffects.getAbility(effect.getId()), game); @@ -408,6 +416,11 @@ public class ContinuousEffects implements Serializable { } } + public void addEffect(ContinuousEffect effect, UUID sourceId, Ability source) { + addEffect(effect, source); + sources.put(effect, sourceId); + } + public void addEffect(ContinuousEffect effect, Ability source) { switch (effect.getEffectType()) { case REPLACEMENT: @@ -460,6 +473,42 @@ public class ContinuousEffects implements Serializable { for (ContinuousEffectsList effectsList : allEffectsLists) { effectsList.clear(); } + sources.clear(); + } + + /** + * Removes all granted effects + */ + public void removeGainedEffects() { + for (Map.Entry source : sources.entrySet()) { + for (ContinuousEffectsList effectsList : allEffectsLists) { + Ability ability = effectsList.getAbility(source.getKey().getId()); + if (ability != null && !ability.getSourceId().equals(source.getValue())) { + effectsList.removeEffect((ContinuousEffect) source.getKey()); + } + } + } + } + + /** + * Removes effects granted by sourceId + * + * @param sourceId + */ + public List removeGainedEffectsForSource(UUID sourceId) { + List effects = new ArrayList(); + for (Map.Entry source : sources.entrySet()) { + if (sourceId.equals(source.getValue())) { + for (ContinuousEffectsList effectsList : allEffectsLists) { + Ability ability = effectsList.getAbility(source.getKey().getId()); + if (ability != null && !ability.getSourceId().equals(source.getValue())) { + effectsList.removeEffect((ContinuousEffect) source.getKey()); + effects.add(source.getKey()); + } + } + } + } + return effects; } } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffectsList.java b/Mage/src/mage/abilities/effects/ContinuousEffectsList.java index 18d350a2263..d944458e1ba 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffectsList.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffectsList.java @@ -108,6 +108,18 @@ public class ContinuousEffectsList extends ArrayList return abilityMap.get(effectId); } + public void removeEffect(T effect) { + for (Iterator i = this.iterator(); i.hasNext();) { + T entry = i.next(); + if (entry.equals(effect)) { + i.remove(); + if (abilityMap.containsKey(effect.getId())) { + abilityMap.remove(effect.getId()); + } + } + } + } + @Override public void clear() { super.clear(); diff --git a/Mage/src/mage/abilities/effects/common/continious/GainAbilityControlledEffect.java b/Mage/src/mage/abilities/effects/common/continious/GainAbilityControlledEffect.java index b5487c39b0e..166ea49c203 100644 --- a/Mage/src/mage/abilities/effects/common/continious/GainAbilityControlledEffect.java +++ b/Mage/src/mage/abilities/effects/common/continious/GainAbilityControlledEffect.java @@ -106,7 +106,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl> extends MageObjectImpl PermanentCard permanent = new PermanentCard(this, controllerId); game.addPermanent(permanent); game.setZone(objectId, Zone.BATTLEFIELD); - game.applyEffects(); + //game.applyEffects(); // magenoxx: this causes bugs permanent.entersBattlefield(sourceId, game); game.applyEffects(); game.fireEvent(new ZoneChangeEvent(permanent, controllerId, fromZone, Zone.BATTLEFIELD)); diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 170ac12fb3f..94a502c497b 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -123,6 +123,7 @@ public interface Game extends MageItem, Serializable { public Player getLosingPlayer(); public void setStateCheckRequired(); public boolean getStateCheckRequired(); + public void resetForSourceId(UUID sourceId); //client event methods public void addTableEventListener(Listener listener); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 1e7988b7d70..e1322a597c7 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -1440,6 +1440,21 @@ public abstract class GameImpl> implements Game, Serializa shortLivingLKI.clear(); } + @Override + public void resetForSourceId(UUID sourceId) { + // make sure that all effects don't touch this card once it returns back to battlefield + // e.g. this prevents effects affect creature with undying return from graveyard + for (ContinuousEffect effect : getContinuousEffects().getLayeredEffects(this)) { + if (effect.getAffectedObjects().contains(sourceId)) { + effect.getAffectedObjects().remove(sourceId); + } + if (effect.getAffectedObjects().contains(sourceId)) { + effect.getAffectedObjects().remove(sourceId); + } + } + getContinuousEffects().removeGainedEffectsForSource(sourceId); + } + @Override public void cheat(UUID ownerId, Map commands) { if (commands != null) { diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index 769f6c28aa2..ac978502e9c 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -365,6 +365,10 @@ public class GameState implements Serializable, Copyable { effects.addEffect(effect, source); } + public void addEffect(ContinuousEffect effect, UUID sourceId, Ability source) { + effects.addEffect(effect, sourceId, source); + } + // public void addMessage(String message) { // this.messages.add(message); // } @@ -440,6 +444,7 @@ public class GameState implements Serializable, Copyable { } } + @Deprecated public void addAbility(Ability ability, MageObject attachedTo) { if (ability instanceof StaticAbility) { if (ability instanceof KickerAbility) { @@ -458,6 +463,25 @@ public class GameState implements Serializable, Copyable { } } + public void addAbility(Ability ability, UUID sourceId, MageObject attachedTo) { + if (ability instanceof StaticAbility) { + if (ability instanceof KickerAbility) { + return; + } + for (Mode mode: ability.getModes().values()) { + for (Effect effect: mode.getEffects()) { + if (effect instanceof ContinuousEffect) { + addEffect((ContinuousEffect)effect, sourceId, ability); + } + } + } + } + else if (ability instanceof TriggeredAbility) { + // TODO: add sources for triggers - the same way as in addEffect: sources + this.triggers.add((TriggeredAbility)ability, attachedTo); + } + } + public void addEmblem(Emblem emblem) { getCommand().add(emblem); for (Ability ability: emblem.getAbilities()) { diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index 64ae58867bc..b7ade78a0f6 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -114,7 +114,11 @@ public interface Permanent extends Card, Controllable { @Deprecated @Override public void addAbility(Ability ability); + @Deprecated public void addAbility(Ability ability, Game game); + public void addAbility(Ability ability, UUID sourceId, Game game); + + public void removeAllAbilities(UUID sourceId, Game game); public void setLoyaltyUsed(boolean used); public boolean isLoyaltyUsed(); diff --git a/Mage/src/mage/game/permanent/PermanentCard.java b/Mage/src/mage/game/permanent/PermanentCard.java index 2f75a9d2af8..3b6eb84d93c 100644 --- a/Mage/src/mage/game/permanent/PermanentCard.java +++ b/Mage/src/mage/game/permanent/PermanentCard.java @@ -29,7 +29,6 @@ package mage.game.permanent; import mage.Constants.Zone; -import mage.abilities.effects.ContinuousEffect; import mage.cards.Card; import mage.cards.LevelerCard; import mage.game.Game; @@ -174,11 +173,7 @@ public class PermanentCard extends PermanentImpl { ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, controllerId, fromZone, toZone); if (!game.replaceEvent(event)) { if (event.getFromZone().equals(Zone.BATTLEFIELD)) { - for (ContinuousEffect effect : game.getContinuousEffects().getLayeredEffects(game)) { - if (effect.getAffectedObjects().contains(getId())) { - effect.getAffectedObjects().remove(getId()); - } - } + game.resetForSourceId(getId()); } Player owner = game.getPlayer(ownerId); game.rememberLKI(objectId, Zone.BATTLEFIELD, this); diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 9d50e8b110e..7d7253c5883 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -182,6 +182,22 @@ public abstract class PermanentImpl> extends CardImpl } } + @Override + public void addAbility(Ability ability, UUID sourceId, Game game) { + if (!abilities.containsKey(ability.getId())) { + Ability copy = ability.copy(); + copy.setControllerId(controllerId); + copy.setSourceId(objectId); + game.getState().addAbility(copy, sourceId, this); + abilities.add(copy); + } + } + + @Override + public void removeAllAbilities(UUID sourceId, Game game) { + getAbilities().clear(); + } + @Override public Counters getCounters() { return counters;