implement [MH3] Arena of Glory and usage of ManaSpentDelayedTriggeredAbility (#12404)

This commit is contained in:
Susucre 2024-06-07 18:34:45 +02:00 committed by GitHub
parent 0cd2fda764
commit 3e18b58cac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 200 additions and 3 deletions

View file

@ -0,0 +1,117 @@
package mage.cards.a;
import mage.Mana;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.common.delayed.ManaSpentDelayedTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
import mage.abilities.costs.common.ExertSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.effects.common.AddContinuousEffectToGame;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.TapSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.effects.mana.BasicManaEffect;
import mage.abilities.keyword.HasteAbility;
import mage.abilities.mana.RedManaAbility;
import mage.abilities.mana.SimpleManaAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
/**
* @author Susucr
*/
public final class ArenaOfGlory extends CardImpl {
private static final FilterPermanent filter = new FilterControlledPermanent(SubType.MOUNTAIN);
private static final Condition condition
= new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0);
public ArenaOfGlory(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
// Arena of Glory enters the battlefield tapped unless you control a Mountain.
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(
new TapSourceEffect(), condition
), "tapped unless you control a Mountain"));
// {T}: Add {R}.
this.addAbility(new RedManaAbility());
// {R}, {T}, Exert Arena of Glory: Add {R}{R}. If that mana is spent on a creature spell, it gains haste until end of turn.
SimpleManaAbility ability = new SimpleManaAbility(
new BasicManaEffect(Mana.RedMana(2)),
new ManaCostsImpl<>("{R}")
);
ability.addEffect(new CreateDelayedTriggeredAbilityEffect(
new ArenaOfGloryDelayedTriggeredAbility()
));
ability.addCost(new TapSourceCost());
ability.addCost(new ExertSourceCost());
this.addAbility(ability);
}
private ArenaOfGlory(final ArenaOfGlory card) {
super(card);
}
@Override
public ArenaOfGlory copy() {
return new ArenaOfGlory(this);
}
}
class ArenaOfGloryDelayedTriggeredAbility extends ManaSpentDelayedTriggeredAbility {
ArenaOfGloryDelayedTriggeredAbility() {
super(
new AddContinuousEffectToGame(
new GainAbilityTargetEffect(
HasteAbility.getInstance(), Duration.EndOfTurn,
"it gains haste until end of turn", true
)
),
StaticFilters.FILTER_SPELL_CREATURE
);
this.usesStack = false;
this.triggerOnlyOnce = false;
setTriggerPhrase("If that mana is spent on a creature spell, ");
}
private ArenaOfGloryDelayedTriggeredAbility(final ArenaOfGloryDelayedTriggeredAbility effect) {
super(effect);
}
@Override
public ArenaOfGloryDelayedTriggeredAbility copy() {
return new ArenaOfGloryDelayedTriggeredAbility(this);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!super.checkTrigger(event, game)) {
return false;
}
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell == null) {
return false;
}
getEffects().setTargetPointer(new FixedTarget(spell.getCard(), game));
return true;
}
}

View file

@ -35,6 +35,7 @@ public final class ModernHorizons3 extends ExpansionSet {
cards.add(new SetCardInfo("Annoyed Altisaur", 284, Rarity.UNCOMMON, mage.cards.a.AnnoyedAltisaur.class)); cards.add(new SetCardInfo("Annoyed Altisaur", 284, Rarity.UNCOMMON, mage.cards.a.AnnoyedAltisaur.class));
cards.add(new SetCardInfo("Arcbound Condor", 81, Rarity.UNCOMMON, mage.cards.a.ArcboundCondor.class)); cards.add(new SetCardInfo("Arcbound Condor", 81, Rarity.UNCOMMON, mage.cards.a.ArcboundCondor.class));
cards.add(new SetCardInfo("Archway of Innovation", 214, Rarity.RARE, mage.cards.a.ArchwayOfInnovation.class)); cards.add(new SetCardInfo("Archway of Innovation", 214, Rarity.RARE, mage.cards.a.ArchwayOfInnovation.class));
cards.add(new SetCardInfo("Arena of Glory", 215, Rarity.RARE, mage.cards.a.ArenaOfGlory.class));
cards.add(new SetCardInfo("Argent Dais", 20, Rarity.RARE, mage.cards.a.ArgentDais.class)); cards.add(new SetCardInfo("Argent Dais", 20, Rarity.RARE, mage.cards.a.ArgentDais.class));
cards.add(new SetCardInfo("Arna Kennerud, Skycaptain", 178, Rarity.MYTHIC, mage.cards.a.ArnaKennerudSkycaptain.class)); cards.add(new SetCardInfo("Arna Kennerud, Skycaptain", 178, Rarity.MYTHIC, mage.cards.a.ArnaKennerudSkycaptain.class));
cards.add(new SetCardInfo("Ashling, Flame Dancer", 115, Rarity.MYTHIC, mage.cards.a.AshlingFlameDancer.class)); cards.add(new SetCardInfo("Ashling, Flame Dancer", 115, Rarity.MYTHIC, mage.cards.a.AshlingFlameDancer.class));

View file

@ -0,0 +1,69 @@
package org.mage.test.cards.single.mh3;
import mage.abilities.keyword.HasteAbility;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class ArenaOfGloryTest extends CardTestPlayerBase {
/**
* {@link mage.cards.a.ArenaOfGlory Arena of Glory}
* Land
* Arena of Glory enters the battlefield tapped unless you control a Mountain.
* {T}: Add {R}.
* {R}, {T}, Exert Arena of Glory: Add {R}{R}. If that mana is spent on a creature spell, it gains haste until end of turn. (An exerted permanent wont untap during your next untap step.)
*/
private static final String arena = "Arena of Glory";
@Test
public void test_NormalManaNoHaste() {
setStrictChooseMode(true);
addCard(Zone.HAND, playerA, "Dwarven Trader"); // vanilla for {R}
addCard(Zone.BATTLEFIELD, playerA, arena);
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader", true);
checkAbility("Dwarven Trader doesn't have haste", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader", HasteAbility.class, false);
setStopAt(5, PhaseStep.BEGIN_COMBAT);
execute();
assertTapped(arena, false);
assertPermanentCount(playerA, "Dwarven Trader", 1);
assertAbility(playerA, "Dwarven Trader", HasteAbility.getInstance(), false);
}
@Test
public void test_TwoCreatureHaste() {
setStrictChooseMode(true);
addCard(Zone.HAND, playerA, "Dwarven Trader"); // vanilla for {R}
addCard(Zone.HAND, playerA, "Mons's Goblin Raiders"); // vanilla for {R}
addCard(Zone.BATTLEFIELD, playerA, arena);
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
activateManaAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{R}");
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader", true);
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Mons's Goblin Raiders", true);
checkAbility("Dwarven Trader has haste", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Dwarven Trader", HasteAbility.class, true);
checkAbility("Mons's Goblin Raiders has haste", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Mons's Goblin Raiders", HasteAbility.class, true);
attack(3, playerA, "Dwarven Trader", playerB);
attack(3, playerA, "Mons's Goblin Raiders", playerB);
setStopAt(5, PhaseStep.BEGIN_COMBAT);
execute();
assertTapped(arena, true); // exerted
assertLife(playerB, 20 - 2);
assertPermanentCount(playerA, "Dwarven Trader", 1);
assertAbility(playerA, "Dwarven Trader", HasteAbility.getInstance(), false);
assertPermanentCount(playerA, "Mons's Goblin Raiders", 1);
assertAbility(playerA, "Mons's Goblin Raiders", HasteAbility.getInstance(), false);
}
}

View file

@ -106,6 +106,7 @@ public class MageObjectReference implements Comparable<MageObjectReference>, Ser
public String toString() { public String toString() {
return "(" + zoneChangeCounter + "|" + sourceId.toString().substring(0, 3) + ")"; return "(" + zoneChangeCounter + "|" + sourceId.toString().substring(0, 3) + ")";
} }
public UUID getSourceId() { public UUID getSourceId() {
return sourceId; return sourceId;
} }
@ -185,6 +186,14 @@ public class MageObjectReference implements Comparable<MageObjectReference>, Ser
return null; return null;
} }
public Spell getSpell(Game game) {
Spell spell = game.getSpell(sourceId);
if (spell != null && spell.getZoneChangeCounter(game) == zoneChangeCounter) {
return spell;
}
return null;
}
public boolean zoneCounterIsCurrent(Game game) { public boolean zoneCounterIsCurrent(Game game) {
return game.getState().getZoneChangeCounter(sourceId) == zoneChangeCounter; return game.getState().getZoneChangeCounter(sourceId) == zoneChangeCounter;
} }

View file

@ -27,7 +27,7 @@ public class ManaSpentDelayedTriggeredAbility extends DelayedTriggeredAbility {
setTriggerPhrase("When you spend this mana to cast " + filter.getMessage() + ", "); setTriggerPhrase("When you spend this mana to cast " + filter.getMessage() + ", ");
} }
private ManaSpentDelayedTriggeredAbility(final ManaSpentDelayedTriggeredAbility ability) { protected ManaSpentDelayedTriggeredAbility(final ManaSpentDelayedTriggeredAbility ability) {
super(ability); super(ability);
this.filter = ability.filter; this.filter = ability.filter;
} }

View file

@ -36,6 +36,7 @@ public class AddContinuousEffectToGame extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
for (Effect effect : this.effects) { for (Effect effect : this.effects) {
effect.setTargetPointer(this.getTargetPointer().copy());
game.addEffect((ContinuousEffect) effect, source); game.addEffect((ContinuousEffect) effect, source);
} }
return true; return true;