mirror of
https://github.com/correl/mage.git
synced 2025-04-09 01:01:06 -09:00
[LTR] Add tom bombadil (#10244)
This commit is contained in:
parent
b1b0cd7d40
commit
493ca37eac
4 changed files with 345 additions and 0 deletions
Mage.Sets/src/mage
Mage/src/main/java/mage/abilities
188
Mage.Sets/src/mage/cards/t/TomBombadil.java
Normal file
188
Mage.Sets/src/mage/cards/t/TomBombadil.java
Normal file
|
@ -0,0 +1,188 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.SagaAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.CountersOnPermanentsCondition;
|
||||
import mage.abilities.decorator.ConditionalContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
|
||||
import mage.abilities.keyword.HexproofAbility;
|
||||
import mage.abilities.keyword.IndestructibleAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
import mage.abilities.hint.common.CountersOnPermanentsHint;
|
||||
|
||||
/**
|
||||
* @author alexander-novo
|
||||
*/
|
||||
public final class TomBombadil extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SAGA);
|
||||
private static final CountersOnPermanentsCondition condition = new CountersOnPermanentsCondition(filter,
|
||||
CounterType.LORE, ComparisonType.MORE_THAN, 3);
|
||||
private static final CountersOnPermanentsHint hint = new CountersOnPermanentsHint(condition);
|
||||
|
||||
public TomBombadil(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{W}{U}{B}{R}{G}");
|
||||
|
||||
addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.GOD);
|
||||
this.subtype.add(SubType.BARD);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// As long as there are four or more lore counters among Sagas you control, Tom
|
||||
// Bombadil has hexproof and indestructible.
|
||||
Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect(
|
||||
new GainAbilitySourceEffect(HexproofAbility.getInstance()),
|
||||
condition,
|
||||
"As long as there are four or more lore counters among Sagas you control, {this} has hexproof"));
|
||||
ability.addEffect(new ConditionalContinuousEffect(
|
||||
new GainAbilitySourceEffect(IndestructibleAbility.getInstance()), condition, "and has indestructible"));
|
||||
this.addAbility(ability.addHint(hint));
|
||||
|
||||
// Whenever the final chapter ability of a Saga you control resolves, reveal
|
||||
// cards from the top of your library until you reveal a Saga card. Put that
|
||||
// card onto the battlefield and the rest on the bottom of your library in a
|
||||
// random order. This ability triggers only once each turn.
|
||||
this.addAbility(new TomBombadilTriggeredAbility().setTriggersOnce(true));
|
||||
}
|
||||
|
||||
private TomBombadil(final TomBombadil card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TomBombadil copy() {
|
||||
return new TomBombadil(this);
|
||||
}
|
||||
}
|
||||
|
||||
class TomBombadilTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public TomBombadilTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new TomBombadilEffect(), false);
|
||||
}
|
||||
|
||||
public TomBombadilTriggeredAbility(final TomBombadilTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TomBombadilTriggeredAbility copy() {
|
||||
return new TomBombadilTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.RESOLVING_ABILITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
// At this point, the stack ability no longer exists, so we can only reference
|
||||
// the ability that it came from. For EventType.RESOLVING_ABILITY, targetID is
|
||||
// the ID of the original ability (on the permanent) that the resolving ability
|
||||
// came from.
|
||||
Optional<Ability> ability_opt = game.getAbility(event.getTargetId(), event.getSourceId());
|
||||
if (!ability_opt.isPresent())
|
||||
return false;
|
||||
|
||||
// Make sure it was a triggered ability (needed for checking if it's a chapter
|
||||
// ability)
|
||||
Ability ability = ability_opt.get();
|
||||
if (!(ability instanceof TriggeredAbility))
|
||||
return false;
|
||||
|
||||
// Make sure it was a chapter ability
|
||||
TriggeredAbility triggeredAbility = (TriggeredAbility) ability;
|
||||
if (!SagaAbility.isChapterAbility(triggeredAbility))
|
||||
return false;
|
||||
|
||||
// There's a chance that the permanent that this abiltiy came from no longer
|
||||
// exists, so try and find it on the battlefield or check last known
|
||||
// information.
|
||||
// This permanent is needed to check if the resolving ability was the final
|
||||
// chapter ability on that permanent.
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
if (permanent == null
|
||||
|| !permanent.isControlledBy(getControllerId())
|
||||
|| !permanent.hasSubtype(SubType.SAGA, game)) {
|
||||
return false;
|
||||
}
|
||||
// Find the max chapter number from that permanent
|
||||
int maxChapter = CardUtil
|
||||
.castStream(permanent.getAbilities(game).stream(), SagaAbility.class)
|
||||
.map(SagaAbility::getMaxChapter)
|
||||
.mapToInt(SagaChapter::getNumber)
|
||||
.sum();
|
||||
// Check if the ability was the last one
|
||||
return SagaAbility.isFinalAbility(triggeredAbility, maxChapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever the final chapter ability of a Saga you control resolves, reveal cards from the top of your library until you reveal a Saga card. Put that card onto the battlefield and the rest on the bottom of your library in a random order.";
|
||||
}
|
||||
}
|
||||
|
||||
// From PrismaticBridgeEffect
|
||||
class TomBombadilEffect extends OneShotEffect {
|
||||
|
||||
public TomBombadilEffect() {
|
||||
super(Outcome.PutCardInPlay);
|
||||
this.staticText = "reveal cards from the top of your library until you reveal a Saga card. Put that card onto the battlefield and the rest on the bottom of your library in a random order";
|
||||
}
|
||||
|
||||
private TomBombadilEffect(final TomBombadilEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TomBombadilEffect copy() {
|
||||
return new TomBombadilEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
Cards toReveal = new CardsImpl();
|
||||
Card toBattlefield = null;
|
||||
for (Card card : controller.getLibrary().getCards(game)) {
|
||||
toReveal.add(card);
|
||||
if (card.hasSubtype(SubType.SAGA, game)) {
|
||||
toBattlefield = card;
|
||||
break;
|
||||
}
|
||||
}
|
||||
controller.revealCards(source, toReveal, game);
|
||||
if (toBattlefield != null) {
|
||||
controller.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game);
|
||||
toReveal.retainZone(Zone.LIBRARY, game);
|
||||
}
|
||||
controller.putCardsOnBottomOfLibrary(toReveal, game, source, false);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Swamp", 276, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS));
|
||||
cards.add(new SetCardInfo("The One Ring", 246, Rarity.MYTHIC, mage.cards.t.TheOneRing.class));
|
||||
cards.add(new SetCardInfo("The Shire", 260, Rarity.RARE, mage.cards.t.TheShire.class));
|
||||
cards.add(new SetCardInfo("Tom Bombadil", 234, Rarity.MYTHIC, mage.cards.t.TomBombadil.class));
|
||||
cards.add(new SetCardInfo("Trailblazer's Boots", 398, Rarity.RARE, mage.cards.t.TrailblazersBoots.class));
|
||||
cards.add(new SetCardInfo("You Cannot Pass!", 38, Rarity.UNCOMMON, mage.cards.y.YouCannotPass.class));
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.constants.ComparisonType;
|
||||
import mage.counters.Counter;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* Condition which counts the number of a particular kind of counter on
|
||||
* permanents, and sees if it exceeds or falls short of a threshold
|
||||
*
|
||||
* @author alexander-novo
|
||||
*/
|
||||
|
||||
public class CountersOnPermanentsCondition implements Condition {
|
||||
// Which permanents to consider counters on
|
||||
public final FilterPermanent filter;
|
||||
// Which counter type to count
|
||||
public final CounterType counterType;
|
||||
// Whether to check if the number of counters exceeds or falls short of the
|
||||
// threshold
|
||||
public final ComparisonType comparisonType;
|
||||
// The threshold to compare against
|
||||
public final int threshold;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param filter Which permanents to consider counters on
|
||||
* @param counterType Which counter type to count
|
||||
* @param comparisonType Whether to check if the number of counters exceeds or
|
||||
* falls short of the threshold
|
||||
* @param threshold The threshold to compare against
|
||||
*/
|
||||
public CountersOnPermanentsCondition(FilterPermanent filter, CounterType counterType, ComparisonType comparisonType,
|
||||
int threshold) {
|
||||
this.filter = filter;
|
||||
this.counterType = counterType;
|
||||
this.comparisonType = comparisonType;
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
int totalCounters = 0;
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(this.filter,
|
||||
source.getControllerId(), source, game)) {
|
||||
for (Counter counter : permanent.getCounters(game).values()) {
|
||||
if (counter.getName().equals(this.counterType.getName())) {
|
||||
totalCounters += counter.getCount();
|
||||
if (ComparisonType.compare(totalCounters, this.comparisonType, this.threshold)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String comparisonText;
|
||||
switch (this.comparisonType) {
|
||||
case MORE_THAN:
|
||||
comparisonText = String.format("%d or more", threshold + 1);
|
||||
break;
|
||||
case EQUAL_TO:
|
||||
comparisonText = String.format("%d or fewer", threshold - 1);
|
||||
break;
|
||||
case FEWER_THAN:
|
||||
comparisonText = String.format("%d", threshold);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("comparison rules for " + this.comparisonType + " missing");
|
||||
|
||||
}
|
||||
return "there are " + comparisonText + this.counterType.getName() + " counters among "
|
||||
+ this.filter.getMessage();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package mage.abilities.hint.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.ComparisonType;
|
||||
import mage.counters.Counter;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.abilities.condition.common.CountersOnPermanentsCondition;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A hint which keeps track of how many counters of a specific type there are
|
||||
* among some type of permanents
|
||||
*
|
||||
* @author alexander-novo
|
||||
*/
|
||||
public class CountersOnPermanentsHint implements Hint {
|
||||
|
||||
// Which permanents to consider counters on
|
||||
public final FilterPermanent filter;
|
||||
// Which counter type to count
|
||||
public final CounterType counterType;
|
||||
|
||||
/**
|
||||
* @param filter Which permanents to consider counters on
|
||||
* @param counterType Which counter type to count
|
||||
*/
|
||||
public CountersOnPermanentsHint(FilterPermanent filter, CounterType counterType) {
|
||||
this.filter = filter;
|
||||
this.counterType = counterType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy parameters from a {@link CountersOnPermanentsCondition}
|
||||
*/
|
||||
public CountersOnPermanentsHint(CountersOnPermanentsCondition condition) {
|
||||
this.filter = condition.filter;
|
||||
this.counterType = condition.counterType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Game game, Ability ability) {
|
||||
int totalCounters = 0;
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(this.filter,
|
||||
ability.getControllerId(), ability, game)) {
|
||||
for (Counter counter : permanent.getCounters(game).values()) {
|
||||
if (counter.getName().equals(this.counterType.getName())) {
|
||||
totalCounters += counter.getCount();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return this.counterType.getName() + " counters among " + this.filter.getMessage() + ": " + totalCounters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hint copy() {
|
||||
return this;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue