[OTJ] Implement Kambal, Profiteering Mayor

This is slightly bugged due to effect creating different tokens
creating them not at the same time.

See Bestial Menace unit test & #10811 for more info
This commit is contained in:
Susucre 2024-04-03 21:45:28 +02:00
parent 4de6b0b173
commit 39008586d0
3 changed files with 242 additions and 0 deletions

View file

@ -0,0 +1,152 @@
package mage.cards.k;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.EntersBattlefieldOneOrMoreTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeBatchEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author Susucr
*/
public final class KambalProfiteeringMayor extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("tokens");
static {
filter.add(TokenPredicate.TRUE);
}
public KambalProfiteeringMayor(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.ADVISOR);
this.power = new MageInt(2);
this.toughness = new MageInt(4);
// Whenever one or more tokens enter the battlefield under your opponents' control, for each of them, create a tapped token that's a copy of it. This ability triggers only once each turn.
this.addAbility(new KambalProfiteeringMayorTriggeredAbility());
// Whenever one or more tokens enter the battlefield under your control, each opponent loses 1 life and you gain 1 life.
Ability ability = new EntersBattlefieldOneOrMoreTriggeredAbility(
new LoseLifeOpponentsEffect(1), filter, TargetController.YOU
);
ability.addEffect(new GainLifeEffect(1).concatBy("and"));
this.addAbility(ability);
}
private KambalProfiteeringMayor(final KambalProfiteeringMayor card) {
super(card);
}
@Override
public KambalProfiteeringMayor copy() {
return new KambalProfiteeringMayor(this);
}
}
class KambalProfiteeringMayorTriggeredAbility extends TriggeredAbilityImpl {
KambalProfiteeringMayorTriggeredAbility() {
super(Zone.BATTLEFIELD, null);
this.setTriggersOnceEachTurn(true);
}
private KambalProfiteeringMayorTriggeredAbility(final KambalProfiteeringMayorTriggeredAbility effect) {
super(effect);
}
@Override
public KambalProfiteeringMayorTriggeredAbility copy() {
return new KambalProfiteeringMayorTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE_BATCH;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeBatchEvent zEvent = (ZoneChangeBatchEvent) event;
Player controller = game.getPlayer(this.controllerId);
List<UUID> tokensIds = zEvent.getEvents()
.stream()
.filter(zce -> zce.getToZone() == Zone.BATTLEFIELD // keep enter the battlefield
&& controller.hasOpponent(zce.getPlayerId(), game)) // & under your opponent's control
.map(ZoneChangeEvent::getTarget)
.filter(Objects::nonNull)
.filter(p -> p instanceof PermanentToken) // collect only tokens
.map(Permanent::getId)
.collect(Collectors.toList());
if (tokensIds.isEmpty()) {
return false;
}
this.getEffects().clear();
this.addEffect(new KambalProfiteeringMayorEffect(tokensIds));
return true;
}
@Override
public String getRule() {
return "Whenever one or more tokens enter the battlefield under your opponents' control, "
+ "for each of them, create a tapped token that's a copy of it. "
+ "This ability triggers only once each turn.";
}
}
class KambalProfiteeringMayorEffect extends OneShotEffect {
private final List<UUID> tokensIds;
KambalProfiteeringMayorEffect(List<UUID> tokensIds) {
super(Outcome.PutCreatureInPlay);
this.tokensIds = new ArrayList<>(tokensIds);
}
private KambalProfiteeringMayorEffect(final KambalProfiteeringMayorEffect effect) {
super(effect);
this.tokensIds = new ArrayList<>(effect.tokensIds);
}
@Override
public KambalProfiteeringMayorEffect copy() {
return new KambalProfiteeringMayorEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
for (UUID tokenId : tokensIds) {
new CreateTokenCopyTargetEffect(source.getControllerId(), null, false, 1, true, false)
.setTargetPointer(new FixedTarget(tokenId))
.apply(game, source);
}
return true;
}
}

View file

@ -145,6 +145,7 @@ public final class OutlawsOfThunderJunction extends ExpansionSet {
cards.add(new SetCardInfo("Jem Lightfoote, Sky Explorer", 209, Rarity.UNCOMMON, mage.cards.j.JemLightfooteSkyExplorer.class));
cards.add(new SetCardInfo("Jolene, Plundering Pugilist", 210, Rarity.UNCOMMON, mage.cards.j.JolenePlunderingPugilist.class));
cards.add(new SetCardInfo("Kaervek, the Punisher", 92, Rarity.RARE, mage.cards.k.KaervekThePunisher.class));
cards.add(new SetCardInfo("Kambal, Profiteering Mayor", 211, Rarity.RARE, mage.cards.k.KambalProfiteeringMayor.class));
cards.add(new SetCardInfo("Kellan Joins Up", 212, Rarity.RARE, mage.cards.k.KellanJoinsUp.class));
cards.add(new SetCardInfo("Kraum, Violent Cacophony", 214, Rarity.UNCOMMON, mage.cards.k.KraumViolentCacophony.class));
cards.add(new SetCardInfo("Lassoed by the Law", 18, Rarity.UNCOMMON, mage.cards.l.LassoedByTheLaw.class));

View file

@ -0,0 +1,89 @@
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 KambalProfiteeringMayorTest extends CardTestPlayerBase {
/**
* {@link mage.cards.k.KambalProfiteeringMayor Kambal, Profiteering Mayor} {1}{W}{B}
* Legendary Creature Human Advisor
* Whenever one or more tokens enter the battlefield under your opponents control, for each of them, create a tapped token thats a copy of it. This ability triggers only once each turn.
* Whenever one or more tokens enter the battlefield under your control, each opponent loses 1 life and you gain 1 life.
* 2/4
*/
private static final String kambal = "Kambal, Profiteering Mayor";
@Test
public void Test_KrenkosCommand() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerB, kambal);
// Create two 1/1 red Goblin creature tokens.
addCard(Zone.HAND, playerA, "Krenko's Command");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Krenko's Command");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20 - 1); // Second Trigger happened
assertLife(playerB, 20 + 1); // Second Trigger happened
assertPermanentCount(playerA, "Goblin Token", 2);
assertPermanentCount(playerB, "Goblin Token", 2);
}
@Test
public void Test_BestialMenace() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerB, kambal);
// Create a 1/1 green Snake creature token, a 2/2 green Wolf creature token, and a 3/3 green Elephant creature token.
addCard(Zone.HAND, playerA, "Bestial Menace");
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bestial Menace");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20 - 1); // Second Trigger happened
assertLife(playerB, 20 + 1); // Second Trigger happened
assertPermanentCount(playerA, "Snake Token", 1);
assertPermanentCount(playerA, "Wolf Token", 1);
assertPermanentCount(playerA, "Elephant Token", 1);
assertPermanentCount(playerB, "Snake Token", 1);
assertPermanentCount(playerB, "Wolf Token", 0); // TODO: this is a bug, should be 1, see #10811
assertPermanentCount(playerB, "Elephant Token", 0); // TODO: this is a bug, should be 1, see #10811
}
@Test
public void Test_Chatterfang() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerB, kambal);
// Create two 1/1 red Goblin creature tokens.
addCard(Zone.HAND, playerA, "Krenko's Command");
// If one or more tokens would be created under your control, those tokens plus that many 1/1 green Squirrel creature tokens are created instead.
addCard(Zone.BATTLEFIELD, playerA, "Chatterfang, Squirrel General");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Krenko's Command");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20 - 1); // Second Trigger happened
assertLife(playerB, 20 + 1); // Second Trigger happened
assertPermanentCount(playerA, "Goblin Token", 2);
assertPermanentCount(playerA, "Squirrel Token", 2);
assertPermanentCount(playerB, "Goblin Token", 2);
assertPermanentCount(playerB, "Squirrel Token", 2);
}
}