forked from External/mage
[WOC] Implement Alela, Cunning Conqueror (#10870)
Add new batch event `DAMAGED_PLAYER_BATCH_ONE_PLAYER`
This commit is contained in:
parent
73dffb8de9
commit
e39e5ee1b0
6 changed files with 305 additions and 9 deletions
129
Mage.Sets/src/mage/cards/a/AlelaCunningConqueror.java
Normal file
129
Mage.Sets/src/mage/cards/a/AlelaCunningConqueror.java
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.FirstSpellOpponentsTurnTriggeredAbility;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.combat.GoadTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.ControllerIdPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamagedBatchEvent;
|
||||
import mage.game.events.DamagedEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.FaerieRogueToken;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public final class AlelaCunningConqueror extends CardImpl {
|
||||
|
||||
public AlelaCunningConqueror(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{B}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.FAERIE);
|
||||
this.subtype.add(SubType.WARLOCK);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Whenever you cast your first spell during each opponent's turn, create a 1/1 black Faerie Rogue creature token with flying.
|
||||
this.addAbility(new FirstSpellOpponentsTurnTriggeredAbility(
|
||||
new CreateTokenEffect(new FaerieRogueToken()), false
|
||||
));
|
||||
|
||||
// Whenever one or more Faeries you control deal combat damage to a player, goad target creature that player controls.
|
||||
TriggeredAbility trigger = new AlelaCunningConquerorTriggeredAbility();
|
||||
this.addAbility(trigger);
|
||||
}
|
||||
|
||||
private AlelaCunningConqueror(final AlelaCunningConqueror card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlelaCunningConqueror copy() {
|
||||
return new AlelaCunningConqueror(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AlelaCunningConquerorTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
AlelaCunningConquerorTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new GoadTargetEffect(), false);
|
||||
}
|
||||
|
||||
private AlelaCunningConquerorTriggeredAbility(final AlelaCunningConquerorTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlelaCunningConquerorTriggeredAbility copy() {
|
||||
return new AlelaCunningConquerorTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER_BATCH_ONE_PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
DamagedBatchEvent dEvent = (DamagedBatchEvent) event;
|
||||
if (dEvent == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<DamagedEvent> events = dEvent
|
||||
.getEvents()
|
||||
.stream()
|
||||
.filter(DamagedEvent::isCombatDamage)
|
||||
.filter(e -> {
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(e.getSourceId());
|
||||
return permanent != null
|
||||
&& permanent.hasSubtype(SubType.FAERIE, game)
|
||||
&& permanent.isControlledBy(this.getControllerId());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (events.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player opponent = game.getPlayer(dEvent.getPlayerId());
|
||||
if (opponent == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature " + opponent.getLogName() + " controls");
|
||||
filter.add(new ControllerIdPredicate(opponent.getId()));
|
||||
this.getTargets().clear();
|
||||
this.addTarget(new TargetCreaturePermanent(filter));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever one or more Faeries you control " +
|
||||
"deal combat damage to a player, goad target creature that player controls.";
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ public final class WildsOfEldraineCommander extends ExpansionSet {
|
|||
this.hasBasicLands = false;
|
||||
|
||||
cards.add(new SetCardInfo("Ajani's Chosen", 59, Rarity.RARE, mage.cards.a.AjanisChosen.class));
|
||||
cards.add(new SetCardInfo("Alela, Cunning Conqueror", 3, Rarity.MYTHIC, mage.cards.a.AlelaCunningConqueror.class));
|
||||
cards.add(new SetCardInfo("Ancestral Mask", 119, Rarity.COMMON, mage.cards.a.AncestralMask.class));
|
||||
cards.add(new SetCardInfo("Angelic Destiny", 60, Rarity.MYTHIC, mage.cards.a.AngelicDestiny.class));
|
||||
cards.add(new SetCardInfo("Arcane Denial", 84, Rarity.COMMON, mage.cards.a.ArcaneDenial.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
package org.mage.test.cards.single.woe;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.permanent.Permanent;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.multiplayer.MultiplayerTriggerTest;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class AlelaCunningConquerorTest extends MultiplayerTriggerTest {
|
||||
|
||||
/**
|
||||
* Alela, Cunning Conqueror
|
||||
* {2}{U}{B}
|
||||
* Legendary Creature — Faerie Warlock
|
||||
* <p>
|
||||
* Flying
|
||||
* <p>
|
||||
* Whenever you cast your first spell during each opponent’s turn, create a 1/1 black Faerie Rogue creature token with flying.
|
||||
* <p>
|
||||
* Whenever one or more Faeries you control deal combat damage to a player, goad target creature that player controls.
|
||||
*/
|
||||
private static final String alela = "Alela, Cunning Conqueror";
|
||||
|
||||
// 2/1 Faerie
|
||||
private static final String pestermite = "Pestermite";
|
||||
// not a Faerie
|
||||
private static final String stormCrow = "Storm Crow";
|
||||
|
||||
// Bears for playerB
|
||||
private static final String bears = "Grizzly Bears";
|
||||
|
||||
// Carp for playerC
|
||||
private static final String carp = "Ancient Carp";
|
||||
|
||||
// Devoted for playerD
|
||||
private static final String devoted = "Devoted Hero";
|
||||
|
||||
|
||||
@Test
|
||||
public void attackSinglePlayer() {
|
||||
setStrictChooseMode(true);
|
||||
addCard(Zone.BATTLEFIELD, playerA, alela);
|
||||
addCard(Zone.BATTLEFIELD, playerA, pestermite);
|
||||
addCard(Zone.BATTLEFIELD, playerB, bears);
|
||||
addCard(Zone.BATTLEFIELD, playerC, carp);
|
||||
addCard(Zone.BATTLEFIELD, playerD, devoted);
|
||||
|
||||
attack(1, playerA, alela, playerC);
|
||||
attack(1, playerA, pestermite, playerC);
|
||||
addTarget(playerA, carp); // One trigger to goad a creature for playerC
|
||||
|
||||
setStopAt(1, PhaseStep.END_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGoadedByPlayer(carp, playerA);
|
||||
assertLife(playerC, 40 - 2 - 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attackTwoPlayers() {
|
||||
//setStrictChooseMode(true); // did not succesfully differentiate the two very similar triggers to order them in the stack.
|
||||
addCard(Zone.BATTLEFIELD, playerA, alela);
|
||||
addCard(Zone.BATTLEFIELD, playerA, pestermite);
|
||||
addCard(Zone.BATTLEFIELD, playerB, bears);
|
||||
addCard(Zone.BATTLEFIELD, playerC, carp);
|
||||
addCard(Zone.BATTLEFIELD, playerD, devoted);
|
||||
|
||||
attack(1, playerA, alela, playerB);
|
||||
attack(1, playerA, pestermite, playerD);
|
||||
|
||||
setStopAt(1, PhaseStep.END_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGoadedByPlayer(bears, playerA);
|
||||
assertGoadedByPlayer(devoted, playerA);
|
||||
assertLife(playerB, 40 - 2);
|
||||
assertLife(playerD, 40 - 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attackWithNotAFaerie() {
|
||||
setStrictChooseMode(true);
|
||||
addCard(Zone.BATTLEFIELD, playerA, alela);
|
||||
addCard(Zone.BATTLEFIELD, playerA, stormCrow);
|
||||
addCard(Zone.BATTLEFIELD, playerB, bears);
|
||||
addCard(Zone.BATTLEFIELD, playerC, carp);
|
||||
addCard(Zone.BATTLEFIELD, playerD, devoted);
|
||||
|
||||
attack(1, playerA, alela, playerB);
|
||||
attack(1, playerA, stormCrow, playerD);
|
||||
addTarget(playerA, bears); // One trigger to goad a creature for playerB
|
||||
|
||||
setStopAt(1, PhaseStep.END_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGoadedByPlayer(bears, playerA);
|
||||
assertLife(playerB, 40 - 2);
|
||||
assertLife(playerD, 40 - 1);
|
||||
}
|
||||
|
||||
private void assertGoadedByPlayer(String attacker, TestPlayer player) {
|
||||
Permanent permanent = getPermanent(attacker);
|
||||
Assert.assertTrue(
|
||||
"Creature should be goaded by " + player.getName(),
|
||||
permanent.getGoadingPlayers().contains(player.getId())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.game;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.*;
|
||||
|
|
@ -44,8 +45,6 @@ import java.util.*;
|
|||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
* <p>
|
||||
|
|
@ -817,21 +816,56 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
|
||||
public void addSimultaneousDamage(DamagedEvent damagedEvent, Game game) {
|
||||
// combine damages per type (player or permanent)
|
||||
boolean flag = false;
|
||||
// This method does look for Batch Event in simultaneousEvents to batch
|
||||
// damagedEvent with. For each kind of batch, either there is a batch
|
||||
// of the proper class fitting the damagedEvent, or there is not.
|
||||
//
|
||||
// If there is not one of the batched event (i.e. if the respective flag is
|
||||
// at false after the loop), then a batched event is created for future
|
||||
// events to be batched with.
|
||||
|
||||
// All damage from any source to anything
|
||||
// the batch is of class DamagedBatchEvent
|
||||
boolean flagBatchAll = false;
|
||||
|
||||
// All damage from any source to a specific player (damagedEvent.getPlayerId())
|
||||
// the batch is of class DamagedPlayerBatchOnePlayerEvent
|
||||
boolean flagBatchForPlayer = false;
|
||||
|
||||
for (GameEvent event : simultaneousEvents) {
|
||||
|
||||
if ((event instanceof DamagedBatchEvent)
|
||||
&& ((DamagedBatchEvent) event).getDamageClazz().isInstance(damagedEvent)) {
|
||||
// old batch
|
||||
|
||||
// existing batch for damage of that damage class.
|
||||
((DamagedBatchEvent) event).addEvent(damagedEvent);
|
||||
flag = true;
|
||||
break;
|
||||
flagBatchAll = true;
|
||||
}
|
||||
|
||||
if (event instanceof DamagedPlayerBatchOnePlayerEvent) {
|
||||
DamagedPlayerBatchOnePlayerEvent eventForPlayer = (DamagedPlayerBatchOnePlayerEvent) event;
|
||||
if (eventForPlayer.getDamageClazz().isInstance(damagedEvent)
|
||||
&& event.getPlayerId().equals(damagedEvent.getPlayerId())) {
|
||||
|
||||
// existing batch for damage of that damage class to the same player
|
||||
eventForPlayer.addEvent(damagedEvent);
|
||||
flagBatchForPlayer = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!flag) {
|
||||
// new batch
|
||||
|
||||
if (!flagBatchAll) {
|
||||
// new batch for any kind of damage, creating a fresh one with damagedEvent inside.
|
||||
addSimultaneousEvent(DamagedBatchEvent.makeEvent(damagedEvent), game);
|
||||
}
|
||||
if (!flagBatchForPlayer && damagedEvent.getPlayerId() != null) {
|
||||
// new batch for damage from any source to the specific damaged player,
|
||||
// creating a fresh one with damagedEvent inside.
|
||||
DamagedBatchEvent event = new DamagedPlayerBatchOnePlayerEvent(damagedEvent.getPlayerId());
|
||||
event.addEvent(damagedEvent);
|
||||
addSimultaneousEvent(event, game);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleEvent(GameEvent event, Game game) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package mage.game.events;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class DamagedPlayerBatchOnePlayerEvent extends DamagedBatchEvent {
|
||||
|
||||
public DamagedPlayerBatchOnePlayerEvent(UUID playerId) {
|
||||
super(EventType.DAMAGED_PLAYER_BATCH_ONE_PLAYER, DamagedPlayerEvent.class);
|
||||
this.setPlayerId(playerId);
|
||||
}
|
||||
}
|
||||
|
|
@ -123,6 +123,11 @@ public class GameEvent implements Serializable {
|
|||
*/
|
||||
DAMAGED_PLAYER_BATCH,
|
||||
|
||||
/* DAMAGED_PLAYER_BATCH_ONE_PLAYER
|
||||
combines all player damaged events for a single player in one single event
|
||||
*/
|
||||
DAMAGED_PLAYER_BATCH_ONE_PLAYER,
|
||||
|
||||
/* DAMAGE_CAUSES_LIFE_LOSS,
|
||||
targetId the id of the damaged player
|
||||
sourceId sourceId of the ability which caused the damage, can be null for default events like combat
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue