mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
Implement [CLB] Aboleth Spawn (#9857)
Update `StackAbility::createSingleCopy` to properly set controller Includes test coverage --------- Co-authored-by: Rowan Gudmundsson <rowan.gudmundsson@oddball.io> Co-authored-by: xenohedron <xenohedron@users.noreply.github.com>
This commit is contained in:
parent
d705fa0e41
commit
76f38201c0
4 changed files with 186 additions and 1 deletions
105
Mage.Sets/src/mage/cards/a/AbolethSpawn.java
Normal file
105
Mage.Sets/src/mage/cards/a/AbolethSpawn.java
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.CopyStackObjectEffect;
|
||||
import mage.abilities.keyword.FlashAbility;
|
||||
import mage.abilities.keyword.WardAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.StackObject;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Rowan-Gudmundsson, xenohedron
|
||||
*/
|
||||
public final class AbolethSpawn extends CardImpl {
|
||||
|
||||
public AbolethSpawn(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
|
||||
|
||||
this.subtype.add(SubType.FISH);
|
||||
this.subtype.add(SubType.HORROR);
|
||||
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Flash
|
||||
this.addAbility(FlashAbility.getInstance());
|
||||
|
||||
// Ward {2}
|
||||
this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"), false));
|
||||
|
||||
// Whenever a creature entering the battlefield under an opponent's control causes a triggered ability of that creature to trigger,
|
||||
// you may copy that ability. You may choose new targets for the copy.
|
||||
this.addAbility(new AbolethSpawnTriggeredAbility());
|
||||
}
|
||||
|
||||
private AbolethSpawn(final AbolethSpawn card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbolethSpawn copy() {
|
||||
return new AbolethSpawn(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AbolethSpawnTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
AbolethSpawnTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new CopyStackObjectEffect(), true);
|
||||
setTriggerPhrase("Whenever a creature entering the battlefield under an opponent's control causes a triggered ability of that creature to trigger, ");
|
||||
}
|
||||
|
||||
private AbolethSpawnTriggeredAbility(final AbolethSpawnTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.TRIGGERED_ABILITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
StackObject stackObject = game.getStack().getStackObject(event.getTargetId());
|
||||
Permanent permanent = game.getPermanent(event.getSourceId());
|
||||
if (stackObject == null || permanent == null || !permanent.isCreature(game)) {
|
||||
return false; // only creatures
|
||||
}
|
||||
if (!game.getOpponents(this.getControllerId()).contains(permanent.getControllerId())) {
|
||||
return false; // only creatures entering under opponent's control
|
||||
}
|
||||
Ability stackAbility = stackObject.getStackAbility();
|
||||
if (!(stackAbility instanceof TriggeredAbility)) {
|
||||
return false;
|
||||
}
|
||||
GameEvent triggerEvent = ((TriggeredAbility) stackAbility).getTriggerEvent();
|
||||
if (triggerEvent == null || triggerEvent.getType() != GameEvent.EventType.ENTERS_THE_BATTLEFIELD) {
|
||||
return false; // only ETB triggers
|
||||
}
|
||||
if (triggerEvent.getSourceId() != permanent.getId()) {
|
||||
return false; // only triggered abilities of that creature
|
||||
}
|
||||
// CopyStackObjectEffect needs value set
|
||||
getEffects().setValue("stackObject", stackObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbolethSpawnTriggeredAbility copy() {
|
||||
return new AbolethSpawnTriggeredAbility(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ public final class CommanderLegendsBattleForBaldursGate extends ExpansionSet {
|
|||
|
||||
cards.add(new SetCardInfo("Aarakocra Sneak", 54, Rarity.COMMON, mage.cards.a.AarakocraSneak.class));
|
||||
cards.add(new SetCardInfo("Abdel Adrian, Gorion's Ward", 2, Rarity.UNCOMMON, mage.cards.a.AbdelAdrianGorionsWard.class));
|
||||
cards.add(new SetCardInfo("Aboleth Spawn", 662, Rarity.RARE, mage.cards.a.AbolethSpawn.class));
|
||||
cards.add(new SetCardInfo("Acolyte of Bahamut", 212, Rarity.UNCOMMON, mage.cards.a.AcolyteOfBahamut.class));
|
||||
cards.add(new SetCardInfo("Aether Gale", 712, Rarity.RARE, mage.cards.a.AetherGale.class));
|
||||
cards.add(new SetCardInfo("Agent of the Iron Throne", 107, Rarity.UNCOMMON, mage.cards.a.AgentOfTheIronThrone.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
package org.mage.test.cards.single.clb;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author xenohedron
|
||||
*/
|
||||
public class AbolethSpawnTest extends CardTestPlayerBase {
|
||||
|
||||
private static final String aboleth = "Aboleth Spawn"; // 2/3 Flash, ward {2}
|
||||
// Whenever a creature entering the battlefield under an opponent's control
|
||||
// causes a triggered ability of that creature to trigger,
|
||||
// you may copy that ability. You may choose new targets for the copy.
|
||||
private static final String sparkmage = "Sparkmage Apprentice"; // 1/1
|
||||
// When Sparkmage Apprentice enters the battlefield, it deals 1 damage to any target.
|
||||
private static final String attendant = "Soul's Attendant"; // 1/1
|
||||
// Whenever another creature enters the battlefield, you may gain 1 life.
|
||||
private static final String hatchling = "Kraken Hatchling"; // 0/4
|
||||
private static final String ridgescale = "Ridgescale Tusker"; // 5/5
|
||||
// When Ridgescale Tusker enters the battlefield, put a +1/+1 counter on each other creature you control.
|
||||
|
||||
|
||||
@Test
|
||||
public void testTriggerCopies() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, aboleth);
|
||||
addCard(Zone.BATTLEFIELD, playerA, hatchling);
|
||||
addCard(Zone.BATTLEFIELD, playerB, attendant);
|
||||
addCard(Zone.HAND, playerB, sparkmage);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, sparkmage);
|
||||
setChoice(playerB, "Whenever another"); // order triggers
|
||||
addTarget(playerB, hatchling); // to deal 1 damage
|
||||
setChoice(playerA, true); // yes to copy sparkmage trigger
|
||||
setChoice(playerA, true); // yes to change targets
|
||||
addTarget(playerA, aboleth); // to deal 1 damage
|
||||
setChoice(playerB, true); // yes to gain 1 life
|
||||
|
||||
setStopAt(2, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertDamageReceived(playerA, hatchling, 1);
|
||||
assertDamageReceived(playerA, aboleth, 1);
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 21);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTriggerController() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, aboleth);
|
||||
addCard(Zone.BATTLEFIELD, playerB, hatchling);
|
||||
addCard(Zone.BATTLEFIELD, playerA, attendant);
|
||||
addCard(Zone.HAND, playerB, ridgescale);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Forest", 5);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, ridgescale);
|
||||
setChoice(playerA, "Whenever another"); // order triggers
|
||||
setChoice(playerA, true); // yes to copy ridgescale trigger
|
||||
setChoice(playerA, true); // yes to gain 1 life
|
||||
|
||||
setStopAt(2, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertCounterCount(playerA, aboleth, CounterType.P1P1, 1);
|
||||
assertCounterCount(playerA, attendant, CounterType.P1P1, 1);
|
||||
assertCounterCount(playerB, hatchling, CounterType.P1P1, 1);
|
||||
assertLife(playerA, 21);
|
||||
assertLife(playerB, 20);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -632,8 +632,10 @@ public class StackAbility extends StackObjectImpl implements Ability {
|
|||
|
||||
@Override
|
||||
public void createSingleCopy(UUID newControllerId, StackObjectCopyApplier applier, MageObjectReferencePredicate newTargetFilterPredicate, Game game, Ability source, boolean chooseNewTargets) {
|
||||
Ability newAbility = this.copy();
|
||||
Ability newAbility = this.ability.copy();
|
||||
newAbility.newId();
|
||||
newAbility.setControllerId(newControllerId);
|
||||
|
||||
StackAbility newStackAbility = new StackAbility(newAbility, newControllerId);
|
||||
game.getStack().push(newStackAbility);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue