mirror of
https://github.com/correl/mage.git
synced 2025-01-12 19:25:44 +00:00
[NCC] Implemented Jolene, the Plunder Queen (#9093)
This commit is contained in:
parent
b52576fcf9
commit
98c554a59f
3 changed files with 334 additions and 0 deletions
199
Mage.Sets/src/mage/cards/j/JoleneThePlunderQueen.java
Normal file
199
Mage.Sets/src/mage/cards/j/JoleneThePlunderQueen.java
Normal file
|
@ -0,0 +1,199 @@
|
|||
package mage.cards.j;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.combat.Combat;
|
||||
import mage.game.events.CreateTokenEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.Token;
|
||||
import mage.game.permanent.token.TreasureToken;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Susucre
|
||||
*/
|
||||
public final class JoleneThePlunderQueen extends CardImpl {
|
||||
|
||||
private static final FilterControlledPermanent filterTreasures = new FilterControlledPermanent(SubType.TREASURE, "treasures");
|
||||
|
||||
public JoleneThePlunderQueen(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.WARRIOR);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Whenever a player attacks one or more of your opponents, that attacking player creates a Treasure token.
|
||||
this.addAbility(new JoleneThePlunderQueenTriggeredAbility());
|
||||
|
||||
// If you would create one or more Treasure tokens, instead create those tokens plus an additional Treasure token.
|
||||
this.addAbility(new SimpleStaticAbility(new JoleneThePlunderQueenReplacementEffect()));
|
||||
|
||||
// Sacrifice five Treasures: Put five +1/+1 counters on Jolene.
|
||||
this.addAbility(new SimpleActivatedAbility(
|
||||
new AddCountersSourceEffect(CounterType.P1P1.createInstance(5)),
|
||||
new SacrificeTargetCost(new TargetControlledPermanent(5, filterTreasures))
|
||||
));
|
||||
}
|
||||
|
||||
private JoleneThePlunderQueen(final JoleneThePlunderQueen card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JoleneThePlunderQueen copy() {
|
||||
return new JoleneThePlunderQueen(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Based loosely on "Breena, the Demagogue" and "Mila, Crafty Companion"'s trigger abilities
|
||||
class JoleneThePlunderQueenTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
JoleneThePlunderQueenTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new JoleneThePlunderQueenCreateTreasureEffect(), false);
|
||||
}
|
||||
|
||||
private JoleneThePlunderQueenTriggeredAbility(final JoleneThePlunderQueenTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JoleneThePlunderQueenTriggeredAbility copy() {
|
||||
return new JoleneThePlunderQueenTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
Combat combat = game.getCombat();
|
||||
UUID joleneController = game.getControllerId(sourceId);
|
||||
Set<UUID> joleneOpponents = game.getOpponents(joleneController);
|
||||
|
||||
// At most one trigger per combat.
|
||||
if(!combat.getAttackers()
|
||||
.stream()
|
||||
.anyMatch(attackerId -> {
|
||||
// The trigger attempts to find at least one (attacker,defender)
|
||||
// for which the defender is one of jolene's controller opponent
|
||||
UUID defenderId = combat.getDefenderId(attackerId);
|
||||
return joleneOpponents.contains(defenderId);
|
||||
})){
|
||||
return false;
|
||||
}
|
||||
|
||||
getEffects().setTargetPointer(new FixedTarget(event.getPlayerId()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever a player attacks one of your opponents, " +
|
||||
"that attacking player creates a Treasure token.";
|
||||
}
|
||||
}
|
||||
|
||||
class JoleneThePlunderQueenCreateTreasureEffect extends OneShotEffect {
|
||||
|
||||
JoleneThePlunderQueenCreateTreasureEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "that attacking player creates a Treasure token";
|
||||
}
|
||||
|
||||
private JoleneThePlunderQueenCreateTreasureEffect(final JoleneThePlunderQueenCreateTreasureEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JoleneThePlunderQueenCreateTreasureEffect copy() {
|
||||
return new JoleneThePlunderQueenCreateTreasureEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID playerId : getTargetPointer().getTargets(game, source)){
|
||||
new TreasureToken().putOntoBattlefield(1, game, source, playerId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Identical to "Xorn"'s Replacement Effect
|
||||
class JoleneThePlunderQueenReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
public JoleneThePlunderQueenReplacementEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Benefit);
|
||||
this.staticText = "If you would create one or more Treasure tokens, instead create those tokens plus an additional Treasure token";
|
||||
}
|
||||
|
||||
private JoleneThePlunderQueenReplacementEffect(final JoleneThePlunderQueenReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JoleneThePlunderQueenReplacementEffect copy() {
|
||||
return new JoleneThePlunderQueenReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.CREATE_TOKEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (event instanceof CreateTokenEvent && source.isControlledBy(event.getPlayerId())) {
|
||||
for (Token token : ((CreateTokenEvent) event).getTokens().keySet()) {
|
||||
if (token.hasSubtype(SubType.TREASURE, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
if (event instanceof CreateTokenEvent) {
|
||||
CreateTokenEvent tokenEvent = (CreateTokenEvent) event;
|
||||
TreasureToken treasureToken = null;
|
||||
Map<Token, Integer> tokens = tokenEvent.getTokens();
|
||||
for (Token token : tokens.keySet()) {
|
||||
if (token instanceof TreasureToken) {
|
||||
treasureToken = (TreasureToken) token;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (treasureToken == null) {
|
||||
treasureToken = new TreasureToken();
|
||||
}
|
||||
tokens.put(treasureToken, tokens.getOrDefault(treasureToken, 0) + 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -168,6 +168,8 @@ public final class NewCapennaCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Izzet Signet", 369, Rarity.UNCOMMON, mage.cards.i.IzzetSignet.class));
|
||||
cards.add(new SetCardInfo("Jailbreak", 17, Rarity.RARE, mage.cards.j.Jailbreak.class));
|
||||
cards.add(new SetCardInfo("Jenara, Asura of War", 343, Rarity.MYTHIC, mage.cards.j.JenaraAsuraOfWar.class));
|
||||
cards.add(new SetCardInfo("Jolene, the Plunder Queen", 73, Rarity.RARE, mage.cards.j.JoleneThePlunderQueen.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Jolene, the Plunder Queen", 173, Rarity.RARE, mage.cards.j.JoleneThePlunderQueen.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Jund Panorama", 408, Rarity.COMMON, mage.cards.j.JundPanorama.class));
|
||||
cards.add(new SetCardInfo("Jungle Shrine", 409, Rarity.UNCOMMON, mage.cards.j.JungleShrine.class));
|
||||
cards.add(new SetCardInfo("Kamiz, Obscura Oculus", 3, Rarity.MYTHIC, mage.cards.k.KamizObscuraOculus.class));
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
|
||||
package org.mage.test.cards.single.ncc;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestCommander4Players;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Susucre
|
||||
*/
|
||||
public class JoleneThePlunderQueenTest extends CardTestCommander4Players {
|
||||
|
||||
/*
|
||||
Jolene, the Plunder Queen {2}{R}{G}
|
||||
Legendary Creature — Human Warrior 2/2
|
||||
|
||||
Whenever a player attacks one or more of your opponents, that attacking player creates a Treasure token.
|
||||
If you would create one or more Treasure tokens, instead create those tokens plus an additional Treasure token.
|
||||
Sacrifice five Treasures: Put five +1/+1 counters on Jolene.
|
||||
*/
|
||||
String jolene = "Jolene, the Plunder Queen";
|
||||
|
||||
/*
|
||||
Elite Vanguard {W}
|
||||
Creature — Human Soldier 2/1
|
||||
*/
|
||||
String vanguard = "Elite Vanguard";
|
||||
|
||||
/*
|
||||
Balduvian Bears {1}{G}
|
||||
Creature — Bear 2/2
|
||||
*/
|
||||
String bear = "Balduvian Bears";
|
||||
|
||||
/**
|
||||
* test with three players:
|
||||
* A with a Jolene and an Elite Vanguard
|
||||
* B, C other players.
|
||||
*
|
||||
* A attacks both B & C, get two treasures.
|
||||
*/
|
||||
@Test
|
||||
public void testAttackingTwoOpponents() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, jolene, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, vanguard, 1);
|
||||
|
||||
attack(1, playerA, jolene, playerB);
|
||||
attack(1, playerA, vanguard, playerC);
|
||||
|
||||
setStopAt(1, PhaseStep.DECLARE_BLOCKERS);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
// 1 attack trigger, +1 Treasure with the replacement effect.
|
||||
assertPermanentCount(playerA, "Treasure Token", 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* test with three players:
|
||||
* A with an Elite Vanguard and a Balduvian Bears
|
||||
* B with a Jolene
|
||||
* C other player.
|
||||
*
|
||||
* A attacks both B & C, get one treasure.
|
||||
*/
|
||||
@Test
|
||||
public void testAttackingJoleneAndAnotherOpponent() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, vanguard, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, bear, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, jolene, 1);
|
||||
|
||||
attack(1, playerA, bear, playerB);
|
||||
attack(1, playerA, vanguard, playerC);
|
||||
|
||||
setStopAt(1, PhaseStep.DECLARE_BLOCKERS);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
// 1 attack trigger, not controlling Jolene so no replacement effect.
|
||||
assertPermanentCount(playerA, "Treasure Token", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* test with three players:
|
||||
* A with an Elite Vanguard
|
||||
* B with a Jolene
|
||||
* C other player.
|
||||
*
|
||||
* A attacks only B, no trigger, no treasure.
|
||||
*/
|
||||
@Test
|
||||
public void testAttackingJoleneOnly() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, vanguard, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, jolene, 1);
|
||||
|
||||
attack(1, playerA, vanguard, playerB);
|
||||
|
||||
setStopAt(1, PhaseStep.DECLARE_BLOCKERS);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
// no trigger, no token.
|
||||
assertPermanentCount(playerA, "Treasure Token", 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* test with three players:
|
||||
* A with a Jolene and an Elite Vanguard
|
||||
* B with a Jolene.
|
||||
* C with a Jolene.
|
||||
*
|
||||
* A attacks both B & C, 3 triggers, 6 treasures.
|
||||
*/
|
||||
@Test
|
||||
public void testEveryoneGotAJolene() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, vanguard, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, jolene, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, jolene, 1);
|
||||
addCard(Zone.BATTLEFIELD, playerC, jolene, 1);
|
||||
|
||||
attack(1, playerA, vanguard, playerB);
|
||||
attack(1, playerA, jolene, playerC);
|
||||
|
||||
setStopAt(1, PhaseStep.DECLARE_BLOCKERS);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
// 3 triggers (1 for each Jolene), +1 Treasure for each with the replacement effect.
|
||||
assertPermanentCount(playerA, "Treasure Token", 6);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue