[ACR] Implement Havi, the All-Father

This commit is contained in:
theelk801 2025-04-23 09:55:01 -04:00
parent ac34d97987
commit 46aa2a043d
5 changed files with 119 additions and 17 deletions

View file

@ -69,8 +69,7 @@ enum BroodcallerScourgePredicate implements ObjectSourcePlayerPredicate<Card> {
.getManaValue()
<= CardUtil
.getEffectValueFromAbility(
input.getSource(), "damage",
Integer.class, 0
);
input.getSource(), "damage", Integer.class
).orElse(0);
}
}

View file

@ -0,0 +1,103 @@
package mage.cards.h;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.DiesThisOrAnotherTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.CardsInControllerGraveyardCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.abilities.keyword.IndestructibleAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreatureCard;
import mage.filter.common.FilterHistoricCard;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class HaviTheAllFather extends CardImpl {
private static final Condition condition = new CardsInControllerGraveyardCondition(
4, new FilterHistoricCard("historic cards")
);
private static final Hint hint = new ValueHint(
"Historic cards in your graveyard", new CardsInControllerGraveyardCount(new FilterHistoricCard())
);
private static final FilterCard filter = new FilterCreatureCard("legendary creature card with lesser mana value from your graveyard");
static {
filter.add(SuperType.LEGENDARY.getPredicate());
filter.add(HaviTheAllFatherPredicate.instance);
}
public HaviTheAllFather(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{G}{W}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.GOD);
this.subtype.add(SubType.WARRIOR);
this.power = new MageInt(6);
this.toughness = new MageInt(6);
// Havi, the All-Father has indestructible as long as there are four or more historic cards in your graveyard.
this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
new GainAbilitySourceEffect(IndestructibleAbility.getInstance()), condition,
"{this} has indestructible as long as there are four or more historic cards in your graveyard"
)).addHint(hint));
// Sage Project -- Whenever Havi or another legendary creature you control dies, return target legendary creature card with lesser mana value from your graveyard to the battlefield tapped.
Ability ability = new DiesThisOrAnotherTriggeredAbility(
new ReturnFromGraveyardToBattlefieldTargetEffect(true),
false, StaticFilters.FILTER_CONTROLLED_CREATURE_LEGENDARY
);
ability.addTarget(new TargetCardInYourGraveyard(filter));
this.addAbility(ability.withFlavorWord("Sage Project"));
}
private HaviTheAllFather(final HaviTheAllFather card) {
super(card);
}
@Override
public HaviTheAllFather copy() {
return new HaviTheAllFather(this);
}
}
enum HaviTheAllFatherPredicate implements ObjectSourcePlayerPredicate<Card> {
instance;
@Override
public boolean apply(ObjectSourcePlayer<Card> input, Game game) {
return input
.getObject()
.getManaValue()
< CardUtil
.getEffectValueFromAbility(
input.getSource(), "creatureDied", Permanent.class
)
.map(MageObject::getManaValue)
.orElse(0);
}
}

View file

@ -152,9 +152,9 @@ public final class AssassinsCreed extends ExpansionSet {
cards.add(new SetCardInfo("Forest", 110, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_USE_VARIOUS));
cards.add(new SetCardInfo("Go for the Throat", 205, Rarity.UNCOMMON, mage.cards.g.GoForTheThroat.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Go for the Throat", 91, Rarity.UNCOMMON, mage.cards.g.GoForTheThroat.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Havi, the All-Father", 146, Rarity.RARE, mage.cards.h.HaviTheAllFather.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Havi, the All-Father", 237, Rarity.RARE, mage.cards.h.HaviTheAllFather.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Havi, the All-Father", 56, Rarity.RARE, mage.cards.h.HaviTheAllFather.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Havi, the All-Father", 146, Rarity.RARE, mage.cards.h.HaviTheAllFather.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Havi, the All-Father", 237, Rarity.RARE, mage.cards.h.HaviTheAllFather.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Havi, the All-Father", 56, Rarity.RARE, mage.cards.h.HaviTheAllFather.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Haystack", 175, Rarity.UNCOMMON, mage.cards.h.Haystack.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Haystack", 5, Rarity.UNCOMMON, mage.cards.h.Haystack.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Haytham Kenway", 57, Rarity.RARE, mage.cards.h.HaythamKenway.class, NON_FULL_USE_VARIOUS));

View file

@ -61,8 +61,12 @@ public class DiesThisOrAnotherTriggeredAbility extends TriggeredAbilityImpl {
return false;
}
// TODO: remove applyFilterOnSource workaround for Basri's Lieutenant
return ((!applyFilterOnSource && zEvent.getTarget().getId().equals(this.getSourceId()))
|| filter.match(zEvent.getTarget(), getControllerId(), this, game));
if ((applyFilterOnSource || !zEvent.getTarget().getId().equals(this.getSourceId()))
&& !filter.match(zEvent.getTarget(), getControllerId(), this, game)) {
return false;
}
this.getEffects().setValue("creatureDied", zEvent.getTarget());
return true;
}
@Override

View file

@ -1131,10 +1131,10 @@ public final class CardUtil {
* Also ensures that spells/abilities that target the same object twice only trigger each "becomes the target" ability once.
* If this is the first attempt at triggering for a given ability targeting a given object,
* this method records that in the game state for later checks by this same method, to not return the same object again.
*
*
* @param checkingReference must be unique for each usage (this.getId().toString() of the TriggeredAbility, or this.getKey() of the watcher)
* @param event the GameEvent.EventType.TARGETED from checkTrigger() or watch()
* @param game the Game from checkTrigger() or watch()
* @param event the GameEvent.EventType.TARGETED from checkTrigger() or watch()
* @param game the Game from checkTrigger() or watch()
* @return the StackObject which targeted the source, or null if already used or not found
*/
public static StackObject findTargetingStackObject(String checkingReference, GameEvent event, Game game) {
@ -2213,17 +2213,13 @@ public final class CardUtil {
return sb.toString();
}
public static <T> T getEffectValueFromAbility(Ability ability, String key, Class<T> clazz) {
return getEffectValueFromAbility(ability, key, clazz, null);
}
public static <T> T getEffectValueFromAbility(Ability ability, String key, Class<T> clazz, T defaultValue) {
public static <T> Optional<T> getEffectValueFromAbility(Ability ability, String key, Class<T> clazz) {
return castStream(
ability.getAllEffects()
.stream()
.map(effect -> effect.getValue(key)),
clazz
).findFirst().orElse(defaultValue);
).findFirst();
}
public static <T> Stream<T> castStream(Collection<?> collection, Class<T> clazz) {