1
0
Fork 0
mirror of https://github.com/correl/mage.git synced 2025-04-09 01:01:06 -09:00

[LTR] Add tom bombadil ()

This commit is contained in:
Alexander Novotny 2023-04-23 10:41:29 -07:00 committed by GitHub
parent b1b0cd7d40
commit 493ca37eac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 345 additions and 0 deletions
Mage.Sets/src/mage
Mage/src/main/java/mage/abilities

View 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;
}
}

View file

@ -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));

View file

@ -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();
}
}

View file

@ -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;
}
}