[DSK] Implement Hauntwoods Shrieker, added support to reveal face down permanents (#13277)

* [DSK] Implement Hauntwoods Shrieker.
* Change RevealedView and PlayerImpl.revealCards to reveal face-down permanents.
* Fix #13273 using the new RevealCards capability.
This commit is contained in:
Grath 2025-02-07 10:18:31 -05:00 committed by GitHub
parent 89be55c816
commit 145bda842b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 123 additions and 10 deletions

View file

@ -7,6 +7,7 @@ import java.io.Serializable;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.Cards; import mage.cards.Cards;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.PermanentCard;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
@ -19,7 +20,11 @@ public class RevealedView implements Serializable {
public RevealedView(String name, Cards cards, Game game) { public RevealedView(String name, Cards cards, Game game) {
this.name = name; this.name = name;
for (Card card : cards.getCards(game)) { for (Card card : cards.getCards(game)) {
this.cards.put(card.getId(), new CardView(card, game)); if (card instanceof PermanentCard && card.isFaceDown(game)) {
this.cards.put(card.getId(), new CardView(card.getMainCard())); // do not use game param, so it will take default card
} else {
this.cards.put(card.getId(), new CardView(card, game));
}
} }
} }

View file

@ -0,0 +1,102 @@
package mage.cards.h;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.keyword.ManifestDreadEffect;
import mage.cards.Card;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.FilterPermanent;
import mage.filter.predicate.card.FaceDownPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
/**
*
* @author Grath
*/
public final class HauntwoodsShrieker extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("face down creature");
static {
filter.add(FaceDownPredicate.instance);
}
public HauntwoodsShrieker(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}");
this.subtype.add(SubType.BEAST);
this.subtype.add(SubType.MUTANT);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Whenever Hauntwoods Shrieker attacks, manifest dread.
this.addAbility(new AttacksTriggeredAbility(new ManifestDreadEffect()));
// {1}{G}: Reveal target face-down permanent. If it's a creature card, you may turn it face up.
Ability ability = new SimpleActivatedAbility(new HauntwoodsShriekerEffect(), new ManaCostsImpl<>("{1}{G}"));
ability.addTarget(new TargetPermanent(filter));
this.addAbility(ability);
}
private HauntwoodsShrieker(final HauntwoodsShrieker card) {
super(card);
}
@Override
public HauntwoodsShrieker copy() {
return new HauntwoodsShrieker(this);
}
}
class HauntwoodsShriekerEffect extends OneShotEffect {
HauntwoodsShriekerEffect() {
super(Outcome.Benefit);
this.staticText = "Reveal target face-down permanent. If it's a creature card, you may turn it face up";
}
private HauntwoodsShriekerEffect(final HauntwoodsShriekerEffect effect) {
super(effect);
}
@Override
public HauntwoodsShriekerEffect copy() {
return new HauntwoodsShriekerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
MageObject mageObject = game.getObject(source);
if (player == null || mageObject == null) {
return false;
}
Permanent faceDownPermanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (faceDownPermanent != null) {
Card trueCard = (Card)faceDownPermanent.getBasicMageObject();
player.revealCards(source, new CardsImpl(faceDownPermanent), game);
if (trueCard.isCreature() && player.chooseUse(
faceDownPermanent.getControllerId() == source.getControllerId() ? Outcome.Benefit : Outcome.Detriment,
"Turn " + trueCard.getName() + " face up?", source, game)) {
return faceDownPermanent.turnFaceUp(source, game, source.getControllerId());
}
} else {
return false;
}
return true;
}
}

View file

@ -180,8 +180,8 @@ public final class DuskmournHouseOfHorror extends ExpansionSet {
cards.add(new SetCardInfo("Hand That Feeds", 139, Rarity.COMMON, mage.cards.h.HandThatFeeds.class)); cards.add(new SetCardInfo("Hand That Feeds", 139, Rarity.COMMON, mage.cards.h.HandThatFeeds.class));
cards.add(new SetCardInfo("Hardened Escort", 16, Rarity.COMMON, mage.cards.h.HardenedEscort.class)); cards.add(new SetCardInfo("Hardened Escort", 16, Rarity.COMMON, mage.cards.h.HardenedEscort.class));
cards.add(new SetCardInfo("Haunted Screen", 250, Rarity.UNCOMMON, mage.cards.h.HauntedScreen.class)); cards.add(new SetCardInfo("Haunted Screen", 250, Rarity.UNCOMMON, mage.cards.h.HauntedScreen.class));
//cards.add(new SetCardInfo("Hauntwoods Shrieker", 182, Rarity.MYTHIC, mage.cards.h.HauntwoodsShrieker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hauntwoods Shrieker", 182, Rarity.MYTHIC, mage.cards.h.HauntwoodsShrieker.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Hauntwoods Shrieker", 349, Rarity.MYTHIC, mage.cards.h.HauntwoodsShrieker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hauntwoods Shrieker", 349, Rarity.MYTHIC, mage.cards.h.HauntwoodsShrieker.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Hedge Shredder", 183, Rarity.RARE, mage.cards.h.HedgeShredder.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hedge Shredder", 183, Rarity.RARE, mage.cards.h.HedgeShredder.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Hedge Shredder", 320, Rarity.RARE, mage.cards.h.HedgeShredder.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hedge Shredder", 320, Rarity.RARE, mage.cards.h.HedgeShredder.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Horrid Vigor", 184, Rarity.COMMON, mage.cards.h.HorridVigor.class)); cards.add(new SetCardInfo("Horrid Vigor", 184, Rarity.COMMON, mage.cards.h.HorridVigor.class));

View file

@ -58,7 +58,7 @@ import java.util.Set;
* entering the battlefield, that card isnt manifested. Its characteristics remain unmodified and it remains in * entering the battlefield, that card isnt manifested. Its characteristics remain unmodified and it remains in
* its previous zone. If it was face up, it remains face up. * its previous zone. If it was face up, it remains face up.
* <p> * <p>
* 701.34g TODO: need support it * 701.34g
* If a manifested permanent thats represented by an instant or sorcery card would turn face up, its controller * If a manifested permanent thats represented by an instant or sorcery card would turn face up, its controller
* reveals it and leaves it face down. Abilities that trigger whenever a permanent is turned face up wont trigger. * reveals it and leaves it face down. Abilities that trigger whenever a permanent is turned face up wont trigger.
* *

View file

@ -7,13 +7,11 @@ import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.keyword.NightboundAbility; import mage.abilities.keyword.NightboundAbility;
import mage.abilities.keyword.TransformAbility; import mage.abilities.keyword.TransformAbility;
import mage.cards.Card; import mage.cards.*;
import mage.cards.LevelerCard;
import mage.cards.ModalDoubleFacedCard;
import mage.cards.SplitCard;
import mage.constants.SpellAbilityType; import mage.constants.SpellAbilityType;
import mage.game.Game; import mage.game.Game;
import mage.game.events.ZoneChangeEvent; import mage.game.events.ZoneChangeEvent;
import mage.players.Player;
import java.util.UUID; import java.util.UUID;
@ -185,7 +183,10 @@ public class PermanentCard extends PermanentImpl {
// 701.34g. If a manifested permanent that's represented by an instant or sorcery card would turn face up, // 701.34g. If a manifested permanent that's represented by an instant or sorcery card would turn face up,
// its controller reveals it and leaves it face down. Abilities that trigger whenever a permanent // its controller reveals it and leaves it face down. Abilities that trigger whenever a permanent
// is turned face up won't trigger. // is turned face up won't trigger.
// TODO: add reveal effect Player player = game.getPlayer(source.getControllerId());
if (player != null) {
player.revealCards(source, new CardsImpl(this), game);
}
return false; return false;
} }
if (super.turnFaceUp(source, game, playerId)) { if (super.turnFaceUp(source, game, playerId)) {

View file

@ -628,6 +628,7 @@ public interface Player extends MageItem, Copyable<Player> {
* <p> * <p>
* Warning, if you use it from continuous effect, then check with extra call * Warning, if you use it from continuous effect, then check with extra call
* isCanLookAtNextTopLibraryCard * isCanLookAtNextTopLibraryCard
* If you use revealCards with face-down permanents, they will be revealed face up.
* *
* @param source * @param source
* @param name * @param name

View file

@ -1910,7 +1910,11 @@ public abstract class PlayerImpl implements Player, Serializable {
int last = cards.size(); int last = cards.size();
for (Card card : cards.getCards(game)) { for (Card card : cards.getCards(game)) {
current++; current++;
sb.append(GameLog.getColoredObjectName(card)); // TODO: see same usage in OfferingAbility for hide card's id (is it needs for reveal too?!) if (card instanceof PermanentCard && card.isFaceDown(game)) {
sb.append(GameLog.getColoredObjectName(card.getMainCard()));
} else {
sb.append(GameLog.getColoredObjectName(card)); // TODO: see same usage in OfferingAbility for hide card's id (is it needs for reveal too?!)
}
if (current < last) { if (current < last) {
sb.append(", "); sb.append(", ");
} }