[DSC] Implement Cramped Vents // Access Maze

This commit is contained in:
theelk801 2025-12-22 16:42:15 -05:00
parent 491bc43e14
commit f23507a57b
3 changed files with 171 additions and 0 deletions

View file

@ -0,0 +1,169 @@
package mage.cards.c;
import mage.MageIdentifier;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.common.UnlockThisDoorTriggeredAbility;
import mage.abilities.costs.Costs;
import mage.abilities.costs.CostsImpl;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardSetInfo;
import mage.cards.RoomCard;
import mage.constants.*;
import mage.game.Controllable;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetOpponentsCreaturePermanent;
import mage.util.CardUtil;
import mage.watchers.Watcher;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class CrampedVentsAccessMaze extends RoomCard {
public CrampedVentsAccessMaze(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, "{3}{B}", "{5}{B}{B}");
// Cramped Vents
// When you unlock this door, this Room deals 6 damage to target creature an opponent controls. You gain life equal to the excess damage dealt this way.
Ability ability = new UnlockThisDoorTriggeredAbility(new CrampedVentsEffect(), false, true);
ability.addTarget(new TargetOpponentsCreaturePermanent());
ability.addWatcher(new AccessMazeWatcher());
this.getLeftHalfCard().addAbility(ability);
// Access Maze
// Once during each of your turns, you may cast a spell from your hand by paying life equal to its mana value rather than paying its mana cost.
this.getRightHalfCard().addAbility(new SimpleStaticAbility(new AccessMazeEffect())
.setIdentifier(MageIdentifier.AccessMazeWatcher));
}
private CrampedVentsAccessMaze(final CrampedVentsAccessMaze card) {
super(card);
}
@Override
public CrampedVentsAccessMaze copy() {
return new CrampedVentsAccessMaze(this);
}
}
class CrampedVentsEffect extends OneShotEffect {
CrampedVentsEffect() {
super(Outcome.Benefit);
staticText = "{this} deals 6 damage to target creature an opponent controls. " +
"You gain life equal to the excess damage dealt this way.";
}
private CrampedVentsEffect(final CrampedVentsEffect effect) {
super(effect);
}
@Override
public CrampedVentsEffect copy() {
return new CrampedVentsEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (permanent == null) {
return false;
}
int excess = permanent.damageWithExcess(6, source, game);
if (excess > 0) {
Optional.ofNullable(source)
.map(Controllable::getControllerId)
.map(game::getPlayer)
.ifPresent(player -> player.gainLife(excess, game, source));
}
return true;
}
}
class AccessMazeEffect extends AsThoughEffectImpl {
AccessMazeEffect() {
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.PlayForFree);
staticText = "once during each of your turns, you may cast a spell from your hand " +
"by paying life equal to its mana value rather than paying its mana cost";
}
private AccessMazeEffect(final AccessMazeEffect effect) {
super(effect);
}
@Override
public AccessMazeEffect copy() {
return new AccessMazeEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (!source.isControlledBy(affectedControllerId) || !game.isActivePlayer(source.getControllerId())) {
return false;
}
Player controller = game.getPlayer(source.getControllerId());
Permanent sourceObject = game.getPermanent(source.getSourceId());
Card card = game.getCard(CardUtil.getMainCardId(game, objectId));
if (controller == null || card == null || sourceObject == null
|| !Zone.HAND.match(game.getState().getZone(card.getId()))
|| AccessMazeWatcher.isAbilityUsed(new MageObjectReference(sourceObject, game), game)) {
return false;
}
Costs costs = new CostsImpl();
costs.add(new PayLifeCost(card.getManaValue()));
controller.setCastSourceIdWithAlternateMana(
source.getSourceId(), null, costs, MageIdentifier.AccessMazeWatcher
);
return true;
}
}
class AccessMazeWatcher extends Watcher {
private final Set<MageObjectReference> usedFrom = new HashSet<>();
public AccessMazeWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.SPELL_CAST
&& event.hasApprovingIdentifier(MageIdentifier.AccessMazeWatcher)) {
usedFrom.add(event.getApprovingObject().getApprovingMageObjectReference());
}
}
@Override
public void reset() {
super.reset();
usedFrom.clear();
}
static boolean isAbilityUsed(MageObjectReference mor, Game game) {
return game
.getState()
.getWatcher(AccessMazeWatcher.class)
.usedFrom
.contains(mor);
}
}

View file

@ -78,6 +78,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet {
cards.add(new SetCardInfo("Convert to Slime", 37, Rarity.RARE, mage.cards.c.ConvertToSlime.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Convert to Slime", 64, Rarity.RARE, mage.cards.c.ConvertToSlime.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Counterspell", 114, Rarity.UNCOMMON, mage.cards.c.Counterspell.class));
cards.add(new SetCardInfo("Cramped Vents // Access Maze", 17, Rarity.RARE, mage.cards.c.CrampedVentsAccessMaze.class));
cards.add(new SetCardInfo("Crawling Sensation", 173, Rarity.UNCOMMON, mage.cards.c.CrawlingSensation.class));
cards.add(new SetCardInfo("Crypt Ghast", 368, Rarity.MYTHIC, mage.cards.c.CryptGhast.class));
cards.add(new SetCardInfo("Culling Ritual", 85, Rarity.RARE, mage.cards.c.CullingRitual.class));

View file

@ -39,6 +39,7 @@ public enum MageIdentifier {
ThundermanDragonWatcher,
LockeTreasureHunterWatcher,
TheFourthDoctorWatcher,
AccessMazeWatcher,
// ----------------------------//
// alternate casts //