forked from External/mage
fix [OTJ] Fortune, Loyal Steed — DelayedAbility's zcc was wrong when started from another trigger (#12154)
This commit is contained in:
parent
fa728eafb1
commit
d8959f1588
11 changed files with 312 additions and 73 deletions
|
|
@ -1,9 +1,11 @@
|
|||
package org.mage.test.cards.abilities.keywords;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.keyword.MenaceAbility;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.watchers.common.SaddledMountWatcher;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
|
@ -20,7 +22,7 @@ public class SaddleTest extends CardTestPlayerBase {
|
|||
Permanent permanent = getPermanent(name);
|
||||
Assert.assertEquals(
|
||||
name + " should " + (saddled ? "" : "not ") + "be saddled",
|
||||
saddled, permanent.isSaddled()
|
||||
saddled, SaddledMountWatcher.hasBeenSaddledThisTurn(new MageObjectReference(permanent.getId(), currentGame), currentGame)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -78,13 +80,10 @@ public class SaddleTest extends CardTestPlayerBase {
|
|||
setChoice(playerA, bear);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Saddle");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
attack(1, playerA, possum, playerB);
|
||||
setChoice(playerA, bear);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,223 @@
|
|||
package org.mage.test.cards.single.otj;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class FortuneLoyalSteedTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.f.FortuneLoyalSteed Fortune, Loyal Steed} {W}
|
||||
* Legendary Creature — Beast Mount
|
||||
* When Fortune, Loyal Steed enters the battlefield, scry 2.
|
||||
* Whenever Fortune attacks while saddled, at end of combat, exile it and up to one creature that saddled it this turn, then return those cards to the battlefield under their owner’s control.
|
||||
* Saddle 1
|
||||
* 2/4
|
||||
*/
|
||||
private static final String fortune = "Fortune, Loyal Steed";
|
||||
|
||||
@Test
|
||||
public void test_Saddling() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, fortune);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Lone Missionary"); // ETB, gain 4 life
|
||||
addCard(Zone.LIBRARY, playerA, "Taiga", 2);
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Saddling choice
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Saddle");
|
||||
attack(1, playerA, fortune, playerB);
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Choose to blink Lone Missionary
|
||||
|
||||
setChoice(playerA, "When {this} enters the battlefield, you gain 4 life"); // stack triggers
|
||||
addTarget(playerA, "Taiga"); // for the scry trigger
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 + 4);
|
||||
assertHandCount(playerA, 0);
|
||||
assertTapped(fortune, false);
|
||||
assertTapped("Lone Missionary", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Saddling_FortuneDies() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, fortune);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Lone Missionary"); // ETB, gain 4 life
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Ankle Biter"); // 1/1 Deathtouch
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Saddling choice
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Saddle");
|
||||
attack(1, playerA, fortune, playerB);
|
||||
block(1, playerB, "Ankle Biter", fortune);
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Choose to blink Lone Missionary
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 + 4);
|
||||
assertGraveyardCount(playerA, fortune, 1);
|
||||
assertTapped("Lone Missionary", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Saddling_FortuneBlinks() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, fortune);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Lone Missionary"); // ETB, gain 4 life
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains");
|
||||
addCard(Zone.HAND, playerA, "Ephemerate");
|
||||
addCard(Zone.LIBRARY, playerA, "Taiga", 2);
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Saddling choice
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Saddle");
|
||||
attack(1, playerA, fortune, playerB);
|
||||
|
||||
castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerA, "Ephemerate", fortune);
|
||||
addTarget(playerA, "Taiga"); // for the scry trigger
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Choose to blink Lone Missionary
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 + 4);
|
||||
assertTapped(fortune, false);
|
||||
assertTapped("Lone Missionary", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Saddling_FortuneBlinksBefore() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, fortune);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Fervor"); // To give haste
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Lone Missionary"); // ETB, gain 4 life
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
addCard(Zone.HAND, playerA, "Ephemerate", 2);
|
||||
addCard(Zone.LIBRARY, playerA, "Taiga", 2);
|
||||
|
||||
// Just to check zcc
|
||||
castSpell(1, PhaseStep.UPKEEP, playerA, "Ephemerate", fortune);
|
||||
addTarget(playerA, "Taiga"); // for the scry trigger
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Saddling choice
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Saddle");
|
||||
attack(1, playerA, fortune, playerB);
|
||||
|
||||
castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerA, "Ephemerate", fortune);
|
||||
addTarget(playerA, "Taiga"); // for the scry trigger
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Choose to blink Lone Missionary
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 + 4);
|
||||
assertTapped(fortune, false);
|
||||
assertTapped("Lone Missionary", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Saddling_FortuneBlinksAfterSaddlingBeforeCombat() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, fortune);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Fervor"); // To give haste
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Lone Missionary"); // ETB, gain 4 life
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains");
|
||||
addCard(Zone.HAND, playerA, "Ephemerate");
|
||||
addCard(Zone.LIBRARY, playerA, "Taiga", 2);
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Saddling choice
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Saddle");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
// That would make Fortune no longer saddled, so not trigger at beginning of combat
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ephemerate", fortune);
|
||||
addTarget(playerA, "Taiga"); // for the scry trigger
|
||||
|
||||
attack(1, playerA, fortune, playerB);
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertTapped(fortune, true);
|
||||
assertTapped("Lone Missionary", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Saddling_FortuneBlinksInResponseOfSaddling() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, fortune);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Fervor"); // To give haste
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Lone Missionary"); // ETB, gain 4 life
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains");
|
||||
addCard(Zone.HAND, playerA, "Ephemerate");
|
||||
addCard(Zone.LIBRARY, playerA, "Taiga", 2);
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Saddling choice
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Saddle");
|
||||
|
||||
// That would make Fortune no longer saddled, so not trigger at beginning of combat
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ephemerate", fortune);
|
||||
addTarget(playerA, "Taiga"); // for the scry trigger
|
||||
|
||||
attack(1, playerA, fortune, playerB);
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertTapped(fortune, true);
|
||||
assertTapped("Lone Missionary", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Saddling_FortuneBlinksTwice() {
|
||||
setStrictChooseMode(true);
|
||||
skipInitShuffling();
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, fortune);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Lone Missionary"); // ETB, gain 4 life
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
addCard(Zone.HAND, playerA, "Ephemerate", 2);
|
||||
addCard(Zone.LIBRARY, playerA, "Taiga", 2);
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Saddling choice
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Saddle");
|
||||
attack(1, playerA, fortune, playerB);
|
||||
|
||||
castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerA, "Ephemerate", fortune, true);
|
||||
addTarget(playerA, "Taiga"); // for the scry trigger
|
||||
|
||||
castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerA, "Ephemerate", fortune);
|
||||
addTarget(playerA, "Taiga"); // for the scry trigger
|
||||
|
||||
setChoice(playerA, "Lone Missionary"); // Choose to blink Lone Missionary
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20 + 4);
|
||||
assertTapped(fortune, false);
|
||||
assertTapped("Lone Missionary", false);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.watchers.common.SaddledMountWatcher;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
|
@ -15,6 +16,7 @@ public class AttacksWhileSaddledTriggeredAbility extends AttacksTriggeredAbility
|
|||
public AttacksWhileSaddledTriggeredAbility(Effect effect) {
|
||||
super(effect);
|
||||
this.setTriggerPhrase("Whenever {this} attacks while saddled, ");
|
||||
this.addWatcher(new SaddledMountWatcher());
|
||||
}
|
||||
|
||||
private AttacksWhileSaddledTriggeredAbility(final AttacksWhileSaddledTriggeredAbility ability) {
|
||||
|
|
@ -31,7 +33,7 @@ public class AttacksWhileSaddledTriggeredAbility extends AttacksTriggeredAbility
|
|||
return super.checkTrigger(event, game)
|
||||
&& Optional
|
||||
.ofNullable(getSourcePermanentIfItStillExists(game))
|
||||
.map(Permanent::isSaddled)
|
||||
.map(p -> SaddledMountWatcher.hasBeenSaddledThisTurn(new MageObjectReference(p, game), game))
|
||||
.orElse(false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.watchers.common.SaddledMountWatcher;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ public enum SaddledCondition implements Condition {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
return Optional
|
||||
.ofNullable(source.getSourcePermanentIfItStillExists(game))
|
||||
.map(Permanent::isSaddled)
|
||||
.map(p -> SaddledMountWatcher.hasBeenSaddledThisTurn(new MageObjectReference(p, game), game))
|
||||
.orElse(false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@ import mage.abilities.common.SimpleActivatedAbility;
|
|||
import mage.abilities.condition.common.SaddledCondition;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.CostImpl;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.hint.ConditionHint;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.HintUtils;
|
||||
import mage.constants.*;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TimingRule;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.filter.predicate.permanent.TappedPredicate;
|
||||
|
|
@ -24,11 +25,10 @@ import mage.watchers.common.SaddledMountWatcher;
|
|||
|
||||
import java.awt.*;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
* @author TheElk801, Susucr
|
||||
*/
|
||||
public class SaddleAbility extends SimpleActivatedAbility {
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ public class SaddleAbility extends SimpleActivatedAbility {
|
|||
private static final Hint hint = new ConditionHint(SaddledCondition.instance, "This permanent is saddled");
|
||||
|
||||
public SaddleAbility(int value) {
|
||||
super(new SaddleEffect(), new SaddleCost(value));
|
||||
super(new SaddleEventEffect(), new SaddleCost(value));
|
||||
this.value = value;
|
||||
this.addHint(hint);
|
||||
this.setTiming(TimingRule.SORCERY);
|
||||
|
|
@ -60,42 +60,36 @@ public class SaddleAbility extends SimpleActivatedAbility {
|
|||
}
|
||||
}
|
||||
|
||||
class SaddleEffect extends ContinuousEffectImpl {
|
||||
class SaddleEventEffect extends OneShotEffect {
|
||||
|
||||
SaddleEffect() {
|
||||
super(Duration.EndOfTurn, Layer.RulesEffects, SubLayer.NA, Outcome.Benefit);
|
||||
SaddleEventEffect() {
|
||||
super(Outcome.Benefit);
|
||||
}
|
||||
|
||||
private SaddleEffect(final SaddleEffect effect) {
|
||||
private SaddleEventEffect(final SaddleEventEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaddleEffect copy() {
|
||||
return new SaddleEffect(this);
|
||||
public SaddleEventEffect copy() {
|
||||
return new SaddleEventEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (source.getSourcePermanentIfItStillExists(game) != null) {
|
||||
game.fireEvent(GameEvent.getEvent(
|
||||
GameEvent.EventType.MOUNT_SADDLED,
|
||||
source.getSourceId(),
|
||||
source, source.getControllerId()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Optional.ofNullable(source.getSourcePermanentIfItStillExists(game))
|
||||
.ifPresent(permanent -> permanent.setSaddled(true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class SaddleCost extends CostImpl {
|
||||
|
||||
|
||||
private static final FilterControlledCreaturePermanent filter
|
||||
= new FilterControlledCreaturePermanent("another untapped creature you control");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbilityImpl;
|
||||
import mage.abilities.DelayedTriggeredAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
|
||||
import mage.abilities.effects.common.ExileSourceEffect;
|
||||
import mage.abilities.effects.common.ExileTargetEffect;
|
||||
import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
|
|
@ -15,6 +16,8 @@ import mage.constants.Zone;
|
|||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -60,7 +63,7 @@ public class UnearthAbility extends ActivatedAbilityImpl {
|
|||
class UnearthDelayedTriggeredAbility extends DelayedTriggeredAbility {
|
||||
|
||||
public UnearthDelayedTriggeredAbility() {
|
||||
super(new ExileSourceEffect());
|
||||
super(new ExileTargetEffect());
|
||||
}
|
||||
|
||||
protected UnearthDelayedTriggeredAbility(final UnearthDelayedTriggeredAbility ability) {
|
||||
|
|
@ -79,7 +82,19 @@ class UnearthDelayedTriggeredAbility extends DelayedTriggeredAbility {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return event.getPlayerId().equals(this.controllerId);
|
||||
if (!event.getPlayerId().equals(this.controllerId)) {
|
||||
return false;
|
||||
}
|
||||
// The delayed trigger source is the card in the graveyard.
|
||||
// So we need to exile the zcc + 1 permanent
|
||||
MageObjectReference object = new MageObjectReference(getSourceId(), getSourceObjectZoneChangeCounter() + 1, game);
|
||||
Permanent permanent = object.getPermanent(game);
|
||||
if (permanent == null || !permanent.isPhasedIn()) {
|
||||
// Triggers, but do nothing.
|
||||
return true;
|
||||
}
|
||||
getEffects().setTargetPointer(new FixedTarget(permanent, game));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.filter.predicate.permanent;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.filter.predicate.ObjectSourcePlayer;
|
||||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||
import mage.game.Game;
|
||||
|
|
@ -17,7 +18,7 @@ public enum SaddledSourceThisTurnPredicate implements ObjectSourcePlayerPredicat
|
|||
@Override
|
||||
public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
|
||||
return SaddledMountWatcher.checkIfSaddledThisTurn(
|
||||
input.getObject(), input.getSource().getSourcePermanentOrLKI(game), game
|
||||
input.getObject(), new MageObjectReference(input.getSourceId(), input.getSource().getSourceObjectZoneChangeCounter(), game), game
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2162,11 +2162,27 @@ public abstract class GameImpl implements Game {
|
|||
delayedAbility.setSourceId(source.getSourceId());
|
||||
delayedAbility.setControllerId(source.getControllerId());
|
||||
}
|
||||
// return addDelayedTriggeredAbility(delayedAbility);
|
||||
DelayedTriggeredAbility newAbility = delayedAbility.copy();
|
||||
newAbility.newId();
|
||||
if (source != null) {
|
||||
newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(source.getSourceId()));
|
||||
// Relevant ruling:
|
||||
// 603.7e If an activated or triggered ability creates a delayed triggered ability,
|
||||
// the source of that delayed triggered ability is the same as the source of that other ability.
|
||||
// The controller of that delayed triggered ability is the player who controlled that other ability as it resolved.
|
||||
// 603.7f If a static ability generates a replacement effect which causes a delayed triggered ability to be created,
|
||||
// the source of that delayed triggered ability is the object with that static ability.
|
||||
// The controller of that delayed triggered ability is the same as the controller of that object at the time
|
||||
// the replacement effect was applied.
|
||||
//
|
||||
// There are two possibility for the zcc:
|
||||
// 1/ the source is an Ability with a valid (not 0) zcc, and we must use the same.
|
||||
int zcc = source.getSourceObjectZoneChangeCounter();
|
||||
if (zcc == 0) {
|
||||
// 2/ the source has not a valid zcc (it is most likely a StaticAbility instantiated at beginning of game)
|
||||
// we use the source objects's zcc
|
||||
zcc = getState().getZoneChangeCounter(source.getSourceId());
|
||||
}
|
||||
newAbility.setSourceObjectZoneChangeCounter(zcc);
|
||||
newAbility.setSourcePermanentTransformCount(this);
|
||||
}
|
||||
newAbility.init(this);
|
||||
|
|
|
|||
|
|
@ -78,10 +78,6 @@ public interface Permanent extends Card, Controllable {
|
|||
|
||||
void setSuspected(boolean value, Game game, Ability source);
|
||||
|
||||
boolean isSaddled();
|
||||
|
||||
void setSaddled(boolean value);
|
||||
|
||||
boolean isPrototyped();
|
||||
|
||||
void setPrototyped(boolean value);
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
protected boolean monstrous;
|
||||
protected boolean renowned;
|
||||
protected boolean suspected;
|
||||
protected boolean saddled;
|
||||
protected boolean manifested = false;
|
||||
protected boolean morphed = false;
|
||||
protected boolean disguised = false;
|
||||
|
|
@ -176,7 +175,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
this.monstrous = permanent.monstrous;
|
||||
this.renowned = permanent.renowned;
|
||||
this.suspected = permanent.suspected;
|
||||
this.saddled = permanent.saddled;
|
||||
this.ringBearerFlag = permanent.ringBearerFlag;
|
||||
this.classLevel = permanent.classLevel;
|
||||
this.goadingPlayers.addAll(permanent.goadingPlayers);
|
||||
|
|
@ -239,7 +237,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
this.maxBlockedBy = 0;
|
||||
this.copy = false;
|
||||
this.goadingPlayers.clear();
|
||||
this.saddled = false;
|
||||
this.loyaltyActivationsAvailable = 1;
|
||||
this.legendRuleApplies = true;
|
||||
this.canBeSacrificed = true;
|
||||
|
|
@ -1730,16 +1727,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSaddled() {
|
||||
return saddled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSaddled(boolean saddled) {
|
||||
this.saddled = saddled;
|
||||
}
|
||||
|
||||
// Used as key for the ring bearer info.
|
||||
private static final String ringbearerInfoKey = "IS_RINGBEARER";
|
||||
|
||||
|
|
|
|||
|
|
@ -10,22 +10,30 @@ import mage.watchers.Watcher;
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
* @author TheElk801, Susucr
|
||||
*/
|
||||
public class SaddledMountWatcher extends Watcher {
|
||||
|
||||
// key: the mount, value: set of creatures which saddled
|
||||
// key: the mount mor, value: set of creatures which saddled (on Saddle Cost payment)
|
||||
private final Map<MageObjectReference, Set<MageObjectReference>> saddleMap = new HashMap<>();
|
||||
|
||||
// set of mount mor actually saddled (on Saddle Ability resolution)
|
||||
private final Set<MageObjectReference> saddledSet = new HashSet<>();
|
||||
|
||||
public SaddledMountWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.SADDLED_MOUNT) {
|
||||
switch (event.getType()) {
|
||||
case SADDLED_MOUNT:
|
||||
saddleMap.computeIfAbsent(new MageObjectReference(event.getSourceId(), game), x -> new HashSet<>())
|
||||
.add(new MageObjectReference(event.getTargetId(), game));
|
||||
break;
|
||||
case MOUNT_SADDLED:
|
||||
saddledSet.add(new MageObjectReference(event.getSourceId(), game));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -33,24 +41,21 @@ public class SaddledMountWatcher extends Watcher {
|
|||
public void reset() {
|
||||
super.reset();
|
||||
saddleMap.clear();
|
||||
saddledSet.clear();
|
||||
}
|
||||
|
||||
public static boolean checkIfSaddledThisTurn(Permanent saddler, Permanent mount, Game game) {
|
||||
return game
|
||||
public static boolean hasBeenSaddledThisTurn(MageObjectReference mountMOR, Game game) {
|
||||
SaddledMountWatcher watcher = game.getState().getWatcher(SaddledMountWatcher.class);
|
||||
return watcher != null && watcher.saddledSet.contains(mountMOR);
|
||||
}
|
||||
|
||||
public static boolean checkIfSaddledThisTurn(Permanent saddler, MageObjectReference mountMOR, Game game) {
|
||||
return hasBeenSaddledThisTurn(mountMOR, game) && game
|
||||
.getState()
|
||||
.getWatcher(SaddledMountWatcher.class)
|
||||
.saddleMap
|
||||
.getOrDefault(new MageObjectReference(mount, game), Collections.emptySet())
|
||||
.getOrDefault(mountMOR, Collections.emptySet())
|
||||
.stream()
|
||||
.anyMatch(mor -> mor.refersTo(saddler, game));
|
||||
}
|
||||
|
||||
public static int getSaddleCount(Permanent vehicle, Game game) {
|
||||
return game
|
||||
.getState()
|
||||
.getWatcher(SaddledMountWatcher.class)
|
||||
.saddleMap
|
||||
.getOrDefault(new MageObjectReference(vehicle, game), Collections.emptySet())
|
||||
.size();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue