-
Notifications
You must be signed in to change notification settings - Fork 748
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[OTC] Implement Felix Five-Boots (#12074)
will need further improvements in #12173 --------- Co-authored-by: Susucre <34709007+Susucre@users.noreply.github.com>
- Loading branch information
1 parent
60d835a
commit 45be74d
Showing
3 changed files
with
260 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package mage.cards.f; | ||
|
||
import mage.MageInt; | ||
import mage.abilities.Ability; | ||
import mage.abilities.common.SimpleStaticAbility; | ||
import mage.abilities.costs.mana.GenericManaCost; | ||
import mage.abilities.effects.ReplacementEffectImpl; | ||
import mage.abilities.keyword.MenaceAbility; | ||
import mage.abilities.keyword.WardAbility; | ||
import mage.cards.CardImpl; | ||
import mage.cards.CardSetInfo; | ||
import mage.constants.*; | ||
import mage.game.Game; | ||
import mage.game.events.BatchEvent; | ||
import mage.game.events.DamagedEvent; | ||
import mage.game.events.GameEvent; | ||
import mage.game.events.NumberOfTriggersEvent; | ||
import mage.game.permanent.Permanent; | ||
import mage.players.Player; | ||
|
||
import java.util.UUID; | ||
|
||
/** | ||
* @author PurpleCrowbar, Susucr | ||
*/ | ||
public final class FelixFiveBoots extends CardImpl { | ||
|
||
public FelixFiveBoots(UUID ownerId, CardSetInfo setInfo) { | ||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{G}{U}"); | ||
|
||
this.supertype.add(SuperType.LEGENDARY); | ||
this.subtype.add(SubType.OOZE, SubType.ROGUE); | ||
this.power = new MageInt(5); | ||
this.toughness = new MageInt(4); | ||
|
||
// Menace | ||
this.addAbility(new MenaceAbility(false)); | ||
|
||
// Ward {2} | ||
this.addAbility(new WardAbility(new GenericManaCost(2), false)); | ||
|
||
// If a creature you control dealing combat damage to a player causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time. | ||
this.addAbility(new SimpleStaticAbility(new FelixFiveBootsEffect())); | ||
} | ||
|
||
private FelixFiveBoots(final FelixFiveBoots card) { | ||
super(card); | ||
} | ||
|
||
@Override | ||
public FelixFiveBoots copy() { | ||
return new FelixFiveBoots(this); | ||
} | ||
} | ||
|
||
class FelixFiveBootsEffect extends ReplacementEffectImpl { | ||
|
||
FelixFiveBootsEffect() { | ||
super(Duration.WhileOnBattlefield, Outcome.Benefit); | ||
staticText = "If a creature you control dealing combat damage to a player causes a triggered ability " + | ||
"of a permanent you control to trigger, that ability triggers an additional time"; | ||
} | ||
|
||
private FelixFiveBootsEffect(final FelixFiveBootsEffect effect) { | ||
super(effect); | ||
} | ||
|
||
@Override | ||
public FelixFiveBootsEffect copy() { | ||
return new FelixFiveBootsEffect(this); | ||
} | ||
|
||
@Override | ||
public boolean checksEventType(GameEvent event, Game game) { | ||
return event.getType() == GameEvent.EventType.NUMBER_OF_TRIGGERS; | ||
} | ||
|
||
@Override | ||
public boolean applies(GameEvent event, Ability source, Game game) { | ||
NumberOfTriggersEvent numberOfTriggersEvent = (NumberOfTriggersEvent) event; | ||
Permanent sourcePermanent = game.getPermanent(numberOfTriggersEvent.getSourceId()); | ||
if (sourcePermanent == null || !sourcePermanent.isControlledBy(source.getControllerId())) { | ||
return false; | ||
} | ||
|
||
GameEvent sourceEvent = numberOfTriggersEvent.getSourceEvent(); | ||
if (sourceEvent == null) { | ||
return false; | ||
} | ||
|
||
if (sourceEvent instanceof DamagedEvent) { | ||
return checkDamagedEvent((DamagedEvent) sourceEvent, source.getControllerId(), game); | ||
} else if (sourceEvent instanceof BatchEvent) { | ||
for (Object singleEventAsObject : ((BatchEvent) sourceEvent).getEvents()) { | ||
if (singleEventAsObject instanceof DamagedEvent | ||
&& checkDamagedEvent((DamagedEvent) singleEventAsObject, source.getControllerId(), game) | ||
) { | ||
// For batch events, if one of the event inside the condition match the condition, | ||
// the effect applies to the whole batch events. | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
// Checks that a given DamagedEvent matches with | ||
// "If a creature you control dealing combat damage to a player" | ||
private static boolean checkDamagedEvent(DamagedEvent event, UUID controllerId, Game game) { | ||
if (event == null) { | ||
return false; | ||
} | ||
UUID sourceId = event.getSourceId(); | ||
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(sourceId); | ||
UUID targetId = event.getTargetId(); | ||
Player playerDealtDamage = game.getPlayer(targetId); | ||
return sourcePermanent != null | ||
&& sourcePermanent.isCreature(game) | ||
&& sourcePermanent.isControlledBy(controllerId) | ||
&& event.isCombatDamage() | ||
&& playerDealtDamage != null; | ||
} | ||
|
||
@Override | ||
public boolean replaceEvent(GameEvent event, Ability source, Game game) { | ||
event.setAmount(event.getAmount() + 1); | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
129 changes: 129 additions & 0 deletions
129
Mage.Tests/src/test/java/org/mage/test/cards/triggers/damage/FelixFiveBootsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package org.mage.test.cards.triggers.damage; | ||
|
||
import mage.constants.PhaseStep; | ||
import mage.constants.Zone; | ||
import mage.counters.CounterType; | ||
import org.junit.Ignore; | ||
import org.junit.Test; | ||
import org.mage.test.serverside.base.CardTestPlayerBase; | ||
|
||
/** | ||
* @author PurpleCrowbar, Susucr | ||
* https://scryfall.com/card/otc/42/felix-five-boots | ||
*/ | ||
public class FelixFiveBootsTest extends CardTestPlayerBase { | ||
|
||
// Trample. Whenever Belligerent Guest deals combat damage to a player, create a Blood token. | ||
private static final String vampire = "Belligerent Guest"; | ||
|
||
@Test | ||
public void testBasicFelixFunctionality() { | ||
setStrictChooseMode(true); | ||
|
||
addCard(Zone.BATTLEFIELD, playerA, "Felix Five-Boots"); | ||
addCard(Zone.BATTLEFIELD, playerA, vampire); | ||
|
||
attack(1, playerA, vampire, playerB); | ||
setChoice(playerA, "Whenever {this} deals combat damage to a player, create a Blood token."); // need to order the triggers | ||
checkStackSize("two triggers", 1, PhaseStep.COMBAT_DAMAGE, playerA, 2); | ||
|
||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); | ||
execute(); | ||
} | ||
|
||
@Test | ||
public void testDoubleTriggerDeadAttacker() { | ||
setStrictChooseMode(true); | ||
|
||
addCard(Zone.BATTLEFIELD, playerA, "Felix Five-Boots"); | ||
addCard(Zone.BATTLEFIELD, playerA, vampire); | ||
addCard(Zone.BATTLEFIELD, playerB, "Moss Viper"); // 1/1 Deathtouch | ||
|
||
attack(1, playerA, vampire, playerB); | ||
block(1, playerB, "Moss Viper", vampire); | ||
|
||
setChoice(playerA, "X=1"); // assign damage to Moss Viper | ||
setChoice(playerA, "Whenever {this} deals combat damage to a player, create a Blood token."); // need to order the triggers | ||
checkStackSize("two triggers", 1, PhaseStep.COMBAT_DAMAGE, playerA, 2); | ||
|
||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); | ||
execute(); | ||
} | ||
|
||
@Test | ||
public void testNoBonusTriggerForEnemy() { | ||
setStrictChooseMode(true); | ||
|
||
addCard(Zone.BATTLEFIELD, playerA, vampire); | ||
addCard(Zone.BATTLEFIELD, playerB, "Felix Five-Boots"); | ||
|
||
attack(1, playerA, vampire, playerB); | ||
checkStackSize("one trigger", 1, PhaseStep.COMBAT_DAMAGE, playerA, 1); | ||
|
||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); | ||
execute(); | ||
} | ||
|
||
@Test | ||
public void testNoTriggerOnNonCombatDamage() { | ||
setStrictChooseMode(true); | ||
|
||
addCard(Zone.BATTLEFIELD, playerA, "Felix Five-Boots"); | ||
addCard(Zone.BATTLEFIELD, playerA, "Nettle Drone"); // {T}: {this} deals 1 damage to each opponent | ||
addCard(Zone.BATTLEFIELD, playerA, "Island"); | ||
addCard(Zone.HAND, playerA, "Curiosity"); // Whenever enchanted creature deals damage to an opponent, you may draw a card | ||
|
||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curiosity", "Nettle Drone", true); | ||
activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{T}: {this} deals 1 damage to each opponent"); | ||
checkStackSize("one trigger", 1, PhaseStep.BEGIN_COMBAT, playerA, 1); | ||
setChoice(playerA, true); // yes to Curiosity "you may draw" | ||
|
||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); | ||
execute(); | ||
} | ||
|
||
@Test | ||
public void testBatchEvent() { | ||
setStrictChooseMode(true); | ||
|
||
addCard(Zone.BATTLEFIELD, playerA, "Felix Five-Boots"); | ||
// Whenever Olivia's Attendants deals damage, create that many Blood tokens. 6/6 | ||
addCard(Zone.BATTLEFIELD, playerA, "Olivia's Attendants"); | ||
|
||
attack(1, playerA, "Olivia's Attendants", playerB); | ||
setChoice(playerA, "Whenever {this} deals damage, create that many Blood tokens."); // need to order the triggers | ||
checkStackSize("two triggers", 1, PhaseStep.COMBAT_DAMAGE, playerA, 2); | ||
|
||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); | ||
execute(); | ||
|
||
assertPermanentCount(playerA, "Blood Token", 6 + 6); | ||
} | ||
|
||
@Test | ||
@Ignore // see #12095 | ||
public void testSelectRightPartOfBatch() { | ||
setStrictChooseMode(true); | ||
|
||
addCard(Zone.BATTLEFIELD, playerA, "Felix Five-Boots"); | ||
// Whenever equipped creature deals combat damage, put two charge counters on Umezawa’s Jitte. | ||
addCard(Zone.BATTLEFIELD, playerA, "Umezawa's Jitte"); | ||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); | ||
addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); | ||
addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin"); | ||
addCard(Zone.BATTLEFIELD, playerB, "Wall of Blossoms"); | ||
|
||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Elite Vanguard"); | ||
|
||
attack(1, playerA, "Elite Vanguard", playerB); | ||
attack(1, playerA, "Raging Goblin", playerB); | ||
block(1, playerB, "Wall of Blossoms", "Elite Vanguard"); | ||
|
||
checkStackSize("only one Jitte triggers", 1, PhaseStep.COMBAT_DAMAGE, playerA, 1); | ||
|
||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); | ||
execute(); | ||
|
||
assertCounterCount(playerA, "Umezawa's Jitte", CounterType.CHARGE, 2); | ||
} | ||
} |