mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
Fixed Issue#35. All tests pass now.
This commit is contained in:
parent
aa071912cb
commit
c61e4f2b32
13 changed files with 230 additions and 18 deletions
|
|
@ -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<DearlyDeparted> {
|
|||
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<DearlyDeparted> {
|
|||
return new DearlyDeparted(this);
|
||||
}
|
||||
}
|
||||
|
||||
class EntersBattlefieldEffect extends ReplacementEffectImpl<EntersBattlefieldEffect> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<T extends AbilityImpl<T>> 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);
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ public class ContinuousEffects implements Serializable {
|
|||
private final AuraReplacementEffect auraReplacementEffect;
|
||||
|
||||
private List<ContinuousEffect> previous = new ArrayList<ContinuousEffect>();
|
||||
|
||||
private Map<Effect, UUID> sources = new HashMap<Effect, UUID>();
|
||||
|
||||
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<Effect, UUID> 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<Effect, UUID> 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<Effect> removeGainedEffectsForSource(UUID sourceId) {
|
||||
List<Effect> effects = new ArrayList<Effect>();
|
||||
for (Map.Entry<Effect, UUID> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,18 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
|
|||
return abilityMap.get(effectId);
|
||||
}
|
||||
|
||||
public void removeEffect(T effect) {
|
||||
for (Iterator<T> 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();
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl<GainAbilit
|
|||
if (!this.affectedObjectsSet || objects.contains(perm.getId())) {
|
||||
if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
|
||||
for (Ability abilityToAdd : ability) {
|
||||
perm.addAbility(abilityToAdd, game);
|
||||
perm.addAbility(abilityToAdd, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public class LoseAllAbilitiesTargetEffect extends ContinuousEffectImpl {
|
|||
for (UUID permanentId : targetPointer.getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(permanentId);
|
||||
if (permanent != null) {
|
||||
permanent.getAbilities().clear();
|
||||
permanent.removeAllAbilities(source.getSourceId(), game);
|
||||
affectedTargets++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -399,7 +399,7 @@ public abstract class CardImpl<T extends CardImpl<T>> extends MageObjectImpl<T>
|
|||
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));
|
||||
|
|
|
|||
|
|
@ -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<TableEvent> listener);
|
||||
|
|
|
|||
|
|
@ -1440,6 +1440,21 @@ public abstract class GameImpl<T extends GameImpl<T>> 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<Zone, String> commands) {
|
||||
if (commands != null) {
|
||||
|
|
|
|||
|
|
@ -365,6 +365,10 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
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<GameState> {
|
|||
}
|
||||
}
|
||||
|
||||
@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<GameState> {
|
|||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<PermanentCard> {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -182,6 +182,22 @@ public abstract class PermanentImpl<T extends PermanentImpl<T>> 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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue